From 5831e275d8565ec2f41d2a82816ad8f6d8c85b66 Mon Sep 17 00:00:00 2001
From: abdalrouf-AAA <236259618+technologyet31-create@users.noreply.github.com>
Date: Sun, 5 Jul 2026 13:49:15 +0200
Subject: [PATCH 1/2] fix: require a trace endpoint's pad to be on the same
layer
checkTracesAreContiguous matched an endpoint to a pad by x/y only, so a
top-layer wire endpoint counted as connected to a bottom-layer pad. Gate
the match on layer: isPointInPad now takes the endpoint's layer as a
separate param and rejects a pad on a different layer (smt pad -> its
layer, plated hole -> its copper layers).
Flips the repro tests from #165 to passing and updates the
shouldDrawErrors snapshot to show the now-flagged endpoint.
Co-authored-by: abdalrouf-AAA <236259618+technologyet31-create@users.noreply.github.com>
---
.../check-traces-are-contiguous.ts | 26 +++++++++++++++----
.../is-point-in-pad.ts | 11 ++++++++
...trace-endpoint-on-different-layer.snap.svg | 2 +-
tests/lib/check-traces-are-contiguous.test.ts | 4 +--
4 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/lib/check-traces-are-contiguous/check-traces-are-contiguous.ts b/lib/check-traces-are-contiguous/check-traces-are-contiguous.ts
index b75e32f..3d5fcc5 100644
--- a/lib/check-traces-are-contiguous/check-traces-are-contiguous.ts
+++ b/lib/check-traces-are-contiguous/check-traces-are-contiguous.ts
@@ -52,7 +52,7 @@ function routePointConnectsToAnotherExpectedPort(
const expectedPads = padMap.get(expectedPort.pcb_port_id)
return (
expectedPads?.some((pad) =>
- isPointInPad({ x: point.x, y: point.y }, pad),
+ isPointInPad({ x: point.x, y: point.y }, pad, point.layer),
) ?? false
)
})
@@ -273,13 +273,21 @@ function checkTracesAreContiguous(
const isFirstPointConnected =
firstPoint.route_type === "wire" &&
pads.some((pad) =>
- isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad),
+ isPointInPad(
+ { x: firstPoint.x, y: firstPoint.y },
+ pad,
+ firstPoint.layer,
+ ),
)
const isLastPointConnected =
lastPoint.route_type === "wire" &&
pads.some((pad) =>
- isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad),
+ isPointInPad(
+ { x: lastPoint.x, y: lastPoint.y },
+ pad,
+ lastPoint.layer,
+ ),
)
if (!isFirstPointConnected && !isLastPointConnected) {
@@ -326,7 +334,11 @@ function checkTracesAreContiguous(
if (
firstPoint.route_type === "wire" &&
pads.some((pad) =>
- isPointInPad({ x: firstPoint.x, y: firstPoint.y }, pad),
+ isPointInPad(
+ { x: firstPoint.x, y: firstPoint.y },
+ pad,
+ firstPoint.layer,
+ ),
)
) {
firstConnectsToAnyPad = true
@@ -334,7 +346,11 @@ function checkTracesAreContiguous(
if (
lastPoint.route_type === "wire" &&
pads.some((pad) =>
- isPointInPad({ x: lastPoint.x, y: lastPoint.y }, pad),
+ isPointInPad(
+ { x: lastPoint.x, y: lastPoint.y },
+ pad,
+ lastPoint.layer,
+ ),
)
) {
lastConnectsToAnyPad = true
diff --git a/lib/check-traces-are-contiguous/is-point-in-pad.ts b/lib/check-traces-are-contiguous/is-point-in-pad.ts
index e786e83..4ec84c3 100644
--- a/lib/check-traces-are-contiguous/is-point-in-pad.ts
+++ b/lib/check-traces-are-contiguous/is-point-in-pad.ts
@@ -34,7 +34,18 @@ function isPointOnSegment(
export function isPointInPad(
point: { x: number; y: number },
pad: PcbSmtPad | PcbPlatedHole,
+ layer?: string,
): boolean {
+ // A trace endpoint only connects to a pad on the same layer (a top-layer
+ // wire can't land on a bottom-layer smt pad without a via).
+ if (layer !== undefined) {
+ const padIsOnLayer =
+ pad.type === "pcb_smtpad"
+ ? pad.layer === layer
+ : pad.layers.some((padLayer) => padLayer === layer)
+ if (!padIsOnLayer) return false
+ }
+
if (pad.type === "pcb_smtpad") {
if (pad.shape === "circle") {
return getDistanceBetweenPoints(point, pad) <= pad.radius
diff --git a/tests/lib/__snapshots__/trace-endpoint-on-different-layer.snap.svg b/tests/lib/__snapshots__/trace-endpoint-on-different-layer.snap.svg
index 41cbce8..baf4829 100644
--- a/tests/lib/__snapshots__/trace-endpoint-on-different-layer.snap.svg
+++ b/tests/lib/__snapshots__/trace-endpoint-on-different-layer.snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/tests/lib/check-traces-are-contiguous.test.ts b/tests/lib/check-traces-are-contiguous.test.ts
index 6af8b7e..539426f 100644
--- a/tests/lib/check-traces-are-contiguous.test.ts
+++ b/tests/lib/check-traces-are-contiguous.test.ts
@@ -410,7 +410,7 @@ test("still reports a source-trace branch that does not reach a required port",
)
})
-test.failing(
+test(
"errors when a trace endpoint touches a pad on a different layer",
() => {
// A top-layer wire endpoint at (1,0) coincides with a BOTTOM-layer pad.
@@ -475,7 +475,7 @@ test.failing(
},
)
-test.failing(
+test(
"draws an error where a trace endpoint touches a pad on a different layer",
() => {
// Same scenario rendered with shouldDrawErrors: a top-layer wire ends on
From 5462ba11944b7cbe37b662f24fc87a44b3963d5f Mon Sep 17 00:00:00 2001
From: abdalrouf-AAA <236259618+technologyet31-create@users.noreply.github.com>
Date: Sat, 1 Aug 2026 20:33:38 +0200
Subject: [PATCH 2/2] Run biome format on the trace-layer test
Co-authored-by: abdalrouf-AAA <236259618+technologyet31-create@users.noreply.github.com>
---
tests/lib/check-traces-are-contiguous.test.ts | 278 +++++++++---------
1 file changed, 136 insertions(+), 142 deletions(-)
diff --git a/tests/lib/check-traces-are-contiguous.test.ts b/tests/lib/check-traces-are-contiguous.test.ts
index 539426f..4901ac3 100644
--- a/tests/lib/check-traces-are-contiguous.test.ts
+++ b/tests/lib/check-traces-are-contiguous.test.ts
@@ -410,148 +410,142 @@ test("still reports a source-trace branch that does not reach a required port",
)
})
-test(
- "errors when a trace endpoint touches a pad on a different layer",
- () => {
- // A top-layer wire endpoint at (1,0) coincides with a BOTTOM-layer pad.
- // They cannot connect without a via, so this should be flagged.
- const circuitJson = [
- {
- type: "source_trace",
- source_trace_id: "trace_1",
- connected_source_port_ids: ["port_1", "port_2"],
- },
- {
- type: "pcb_port",
- pcb_port_id: "pp1",
- source_port_id: "port_1",
- x: 0,
- y: 0,
- layers: ["top"],
- },
- {
- type: "pcb_port",
- pcb_port_id: "pp2",
- source_port_id: "port_2",
- x: 1,
- y: 0,
- layers: ["top"],
- },
- {
- type: "pcb_smtpad",
- pcb_smtpad_id: "pad_1",
- pcb_port_id: "pp1",
- shape: "rect",
- x: 0,
- y: 0,
- width: 0.5,
- height: 0.5,
- layer: "top",
- },
- {
- type: "pcb_smtpad",
- pcb_smtpad_id: "pad_2",
- pcb_port_id: "pp2",
- shape: "rect",
- x: 1,
- y: 0,
- width: 0.5,
- height: 0.5,
- layer: "bottom",
- },
- {
- type: "pcb_trace",
- pcb_trace_id: "t1",
- source_trace_id: "trace_1",
- route: [
- { route_type: "wire", x: 0, y: 0, layer: "top", width: 0.2 },
- { route_type: "wire", x: 1, y: 0, layer: "top", width: 0.2 },
- ],
- },
- ] as AnyCircuitElement[]
+test("errors when a trace endpoint touches a pad on a different layer", () => {
+ // A top-layer wire endpoint at (1,0) coincides with a BOTTOM-layer pad.
+ // They cannot connect without a via, so this should be flagged.
+ const circuitJson = [
+ {
+ type: "source_trace",
+ source_trace_id: "trace_1",
+ connected_source_port_ids: ["port_1", "port_2"],
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pp1",
+ source_port_id: "port_1",
+ x: 0,
+ y: 0,
+ layers: ["top"],
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pp2",
+ source_port_id: "port_2",
+ x: 1,
+ y: 0,
+ layers: ["top"],
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pad_1",
+ pcb_port_id: "pp1",
+ shape: "rect",
+ x: 0,
+ y: 0,
+ width: 0.5,
+ height: 0.5,
+ layer: "top",
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pad_2",
+ pcb_port_id: "pp2",
+ shape: "rect",
+ x: 1,
+ y: 0,
+ width: 0.5,
+ height: 0.5,
+ layer: "bottom",
+ },
+ {
+ type: "pcb_trace",
+ pcb_trace_id: "t1",
+ source_trace_id: "trace_1",
+ route: [
+ { route_type: "wire", x: 0, y: 0, layer: "top", width: 0.2 },
+ { route_type: "wire", x: 1, y: 0, layer: "top", width: 0.2 },
+ ],
+ },
+ ] as AnyCircuitElement[]
- const errors = checkTracesAreContiguous(circuitJson)
- expect(errors.length).toBeGreaterThan(0)
- },
-)
+ const errors = checkTracesAreContiguous(circuitJson)
+ expect(errors.length).toBeGreaterThan(0)
+})
-test(
- "draws an error where a trace endpoint touches a pad on a different layer",
- () => {
- // Same scenario rendered with shouldDrawErrors: a top-layer wire ends on
- // the BOTTOM-layer pad on the right. On unfixed code the check misses it,
- // so no error is drawn; the fix makes the error marker appear.
- const circuitJson = [
- {
- type: "pcb_board",
- pcb_board_id: "board_1",
- center: { x: 0.5, y: 0 },
- width: 3,
- height: 2,
- thickness: 1.6,
- num_layers: 2,
- material: "fr4",
- },
- {
- type: "source_trace",
- source_trace_id: "trace_1",
- connected_source_port_ids: ["port_1", "port_2"],
- },
- {
- type: "pcb_port",
- pcb_port_id: "pp1",
- source_port_id: "port_1",
- x: 0,
- y: 0,
- layers: ["top"],
- },
- {
- type: "pcb_port",
- pcb_port_id: "pp2",
- source_port_id: "port_2",
- x: 1,
- y: 0,
- layers: ["top"],
- },
- {
- type: "pcb_smtpad",
- pcb_smtpad_id: "pad_1",
- pcb_port_id: "pp1",
- shape: "rect",
- x: 0,
- y: 0,
- width: 0.5,
- height: 0.5,
- layer: "top",
- },
- {
- type: "pcb_smtpad",
- pcb_smtpad_id: "pad_2",
- pcb_port_id: "pp2",
- shape: "rect",
- x: 1,
- y: 0,
- width: 0.5,
- height: 0.5,
- layer: "bottom",
- },
- {
- type: "pcb_trace",
- pcb_trace_id: "t1",
- source_trace_id: "trace_1",
- route: [
- { route_type: "wire", x: 0, y: 0, layer: "top", width: 0.2 },
- { route_type: "wire", x: 1, y: 0, layer: "top", width: 0.2 },
- ],
- },
- ] as AnyCircuitElement[]
+test("draws an error where a trace endpoint touches a pad on a different layer", () => {
+ // Same scenario rendered with shouldDrawErrors: a top-layer wire ends on
+ // the BOTTOM-layer pad on the right. On unfixed code the check misses it,
+ // so no error is drawn; the fix makes the error marker appear.
+ const circuitJson = [
+ {
+ type: "pcb_board",
+ pcb_board_id: "board_1",
+ center: { x: 0.5, y: 0 },
+ width: 3,
+ height: 2,
+ thickness: 1.6,
+ num_layers: 2,
+ material: "fr4",
+ },
+ {
+ type: "source_trace",
+ source_trace_id: "trace_1",
+ connected_source_port_ids: ["port_1", "port_2"],
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pp1",
+ source_port_id: "port_1",
+ x: 0,
+ y: 0,
+ layers: ["top"],
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pp2",
+ source_port_id: "port_2",
+ x: 1,
+ y: 0,
+ layers: ["top"],
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pad_1",
+ pcb_port_id: "pp1",
+ shape: "rect",
+ x: 0,
+ y: 0,
+ width: 0.5,
+ height: 0.5,
+ layer: "top",
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pad_2",
+ pcb_port_id: "pp2",
+ shape: "rect",
+ x: 1,
+ y: 0,
+ width: 0.5,
+ height: 0.5,
+ layer: "bottom",
+ },
+ {
+ type: "pcb_trace",
+ pcb_trace_id: "t1",
+ source_trace_id: "trace_1",
+ route: [
+ { route_type: "wire", x: 0, y: 0, layer: "top", width: 0.2 },
+ { route_type: "wire", x: 1, y: 0, layer: "top", width: 0.2 },
+ ],
+ },
+ ] as AnyCircuitElement[]
- const errors = checkTracesAreContiguous(circuitJson)
- expect(
- convertCircuitJsonToPcbSvg([...circuitJson, ...errors], {
- shouldDrawErrors: true,
- }),
- ).toMatchSvgSnapshot(import.meta.path, "trace-endpoint-on-different-layer")
- expect(errors.length).toBeGreaterThan(0)
- },
-)
+ const errors = checkTracesAreContiguous(circuitJson)
+ expect(
+ convertCircuitJsonToPcbSvg([...circuitJson, ...errors], {
+ shouldDrawErrors: true,
+ }),
+ ).toMatchSvgSnapshot(import.meta.path, "trace-endpoint-on-different-layer")
+ expect(errors.length).toBeGreaterThan(0)
+})