From f9c0bb0496a6c86eda514d617f615a90e3a8491d Mon Sep 17 00:00:00 2001 From: Jiho Lee Date: Fri, 24 Jul 2026 20:12:42 +0900 Subject: [PATCH] =?UTF-8?q?test:=20repro=20#655=20=E2=80=94=20net=20labels?= =?UTF-8?q?=20are=20placed=20inside=20chip=20bodies?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pins the current behavior on bug-report-20260707T230831Z: the VIN, PG, VOUT and 'U1.FB to R2.pin1' net labels all end up 100% inside chip bodies, rendering behind the components. NetLabelNetLabelCollisionSolver only relocates labels that overlap another label; a label inside a chip without a label-label collision is never checked against chips. Several chips on this board also overlap each other, so port-only labels anchored on covered pins collide in every orientation at their pin anchor. A fix should flip the assertion to .toEqual([]). --- .../repro-net-label-inside-chip-655.test.ts | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/repros/repro-net-label-inside-chip-655.test.ts diff --git a/tests/repros/repro-net-label-inside-chip-655.test.ts b/tests/repros/repro-net-label-inside-chip-655.test.ts new file mode 100644 index 000000000..3f6e7af01 --- /dev/null +++ b/tests/repros/repro-net-label-inside-chip-655.test.ts @@ -0,0 +1,74 @@ +import { expect, test } from "bun:test" +import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver" +import "tests/fixtures/matcher" +import inputProblem from "../bug-reports/bug-report-20260707T230831Z/bug-report-20260707T230831Z.json" + +// Reproduction for https://github.com/tscircuit/schematic-trace-solver/issues/655 +// +// A net label must never render on top of a chip body — it ends up drawn +// behind the component and is illegible. On this board the final placement +// leaves four net labels 100% inside chip bodies: +// +// VIN inside schematic_component_0 (U1) +// U1.FB to R2.pin1 inside schematic_component_4 (R1) +// PG inside schematic_component_0 (U1) +// VOUT inside schematic_component_6 (R3) +// +// NetLabelNetLabelCollisionSolver only relocates a label when it overlaps +// *another label* (findNextCollidingPair looks for label-label overlaps). +// A label sitting inside a chip without a label-label collision is never +// checked against chips or moved out. Additionally, several chips on this +// board overlap each other, so a pin can be covered by another chip's body — +// port-only labels anchored on such pins collide in every orientation. +// +// This test pins the CURRENT (buggy) behavior so the bug is tracked by CI. +// A fix should flip the assertion to .toEqual([]). +test("repro #655: net labels are placed inside chip bodies", () => { + const solver = new SchematicTracePipelineSolver(inputProblem as any) + solver.solve() + + const labels = + solver.netLabelNetLabelCollisionSolver!.getOutput().netLabelPlacements + const chips = solver.inputProblem.chips + + const rectBounds = ( + center: { x: number; y: number }, + width: number, + height: number, + ) => ({ + minX: center.x - width / 2, + maxX: center.x + width / 2, + minY: center.y - height / 2, + maxY: center.y + height / 2, + }) + + const overlapArea = ( + a: { minX: number; maxX: number; minY: number; maxY: number }, + b: { minX: number; maxX: number; minY: number; maxY: number }, + ) => { + const ox = Math.min(a.maxX, b.maxX) - Math.max(a.minX, b.minX) + const oy = Math.min(a.maxY, b.maxY) - Math.max(a.minY, b.minY) + return ox > 0 && oy > 0 ? ox * oy : 0 + } + + const labelsInsideChips = labels.filter((label) => { + const labelBounds = rectBounds(label.center, label.width, label.height) + const labelArea = label.width * label.height + return chips.some( + (chip) => + overlapArea( + labelBounds, + rectBounds(chip.center, chip.width, chip.height), + ) > + labelArea * 0.5, + ) + }) + + // BUG: four labels render fully inside chip bodies + expect(labelsInsideChips.map((label) => label.netId).sort()).toEqual([ + "PG", + "U1.FB to R2.pin1", + "VIN", + "VOUT", + ]) +})