diff --git a/.gitignore b/.gitignore
index c323f1d..a2e71c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -186,3 +186,6 @@ static-server
.yalc
yalc.lock
.vercel
+*.diff.png
+
+test.json
\ No newline at end of file
diff --git a/algos/infinite-grid-ijump-astar/tests/__snapshots__/multilayer-trace.snap.svg b/algos/infinite-grid-ijump-astar/tests/__snapshots__/multilayer-trace.snap.svg
new file mode 100644
index 0000000..2b10fb7
--- /dev/null
+++ b/algos/infinite-grid-ijump-astar/tests/__snapshots__/multilayer-trace.snap.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/algos/infinite-grid-ijump-astar/tests/multilayer-trace.test.ts b/algos/infinite-grid-ijump-astar/tests/multilayer-trace.test.ts
new file mode 100644
index 0000000..d2b1315
--- /dev/null
+++ b/algos/infinite-grid-ijump-astar/tests/multilayer-trace.test.ts
@@ -0,0 +1,356 @@
+import { getSimpleRouteJson } from "autorouting-dataset"
+import { test, expect } from "bun:test"
+import { circuitJsonToPcbSvg } from "circuit-to-svg"
+import { IJumpAutorouter } from "../v2"
+
+const soup: any = [
+ {
+ type: "source_port",
+ source_port_id: "source_port_0",
+ name: "pin1",
+ pin_number: 1,
+ port_hints: ["-", "left", "pin1", "1"],
+ source_component_id: "source_component_0",
+ },
+ {
+ type: "source_port",
+ source_port_id: "source_port_1",
+ name: "pin2",
+ pin_number: 2,
+ port_hints: ["+", "right", "pin2", "2"],
+ source_component_id: "source_component_0",
+ },
+ {
+ type: "source_component",
+ source_component_id: "source_component_0",
+ ftype: "simple_resistor",
+ name: "R1",
+ resistance: 10000,
+ },
+ {
+ type: "source_port",
+ source_port_id: "source_port_2",
+ name: "pin1",
+ pin_number: 1,
+ port_hints: ["-", "left", "pin1", "1"],
+ source_component_id: "source_component_1",
+ },
+ {
+ type: "source_port",
+ source_port_id: "source_port_3",
+ name: "pin2",
+ pin_number: 2,
+ port_hints: ["+", "right", "pin2", "2"],
+ source_component_id: "source_component_1",
+ },
+ {
+ type: "source_component",
+ source_component_id: "source_component_1",
+ ftype: "simple_resistor",
+ name: "R2",
+ resistance: 10000,
+ },
+ {
+ type: "source_port",
+ source_port_id: "source_port_4",
+ name: "pin1",
+ pin_number: 1,
+ port_hints: ["-", "left", "pin1", "1"],
+ source_component_id: "source_component_2",
+ },
+ {
+ type: "source_port",
+ source_port_id: "source_port_5",
+ name: "pin2",
+ pin_number: 2,
+ port_hints: ["+", "right", "pin2", "2"],
+ source_component_id: "source_component_2",
+ },
+ {
+ type: "source_component",
+ source_component_id: "source_component_2",
+ ftype: "simple_resistor",
+ name: "R_obstacle",
+ resistance: 10000,
+ },
+ {
+ type: "source_trace",
+ source_trace_id: "source_trace_0",
+ connected_source_port_ids: ["source_port_1", "source_port_2"],
+ connected_source_net_ids: [],
+ },
+ {
+ type: "schematic_component",
+ schematic_component_id: "schematic_component_0",
+ center: { x: 0, y: 0 },
+ rotation: 0,
+ size: { width: 1, height: 0.55 },
+ source_component_id: "source_component_0",
+ symbol_name: "boxresistor_horz",
+ },
+ {
+ type: "schematic_component",
+ schematic_component_id: "schematic_component_1",
+ center: { x: 0, y: 0 },
+ rotation: 0,
+ size: { width: 1, height: 0.55 },
+ source_component_id: "source_component_1",
+ symbol_name: "boxresistor_horz",
+ },
+ {
+ type: "schematic_component",
+ schematic_component_id: "schematic_component_2",
+ center: { x: 0, y: 0 },
+ rotation: 0,
+ size: { width: 1, height: 0.55 },
+ source_component_id: "source_component_2",
+ symbol_name: "boxresistor_horz",
+ },
+ {
+ type: "schematic_port",
+ schematic_port_id: "schematic_port_0",
+ schematic_component_id: "schematic_component_0",
+ center: { x: -0.5, y: 0 },
+ source_port_id: "source_port_0",
+ facing_direction: "left",
+ },
+ {
+ type: "schematic_port",
+ schematic_port_id: "schematic_port_1",
+ schematic_component_id: "schematic_component_0",
+ center: { x: 0.5, y: 0 },
+ source_port_id: "source_port_1",
+ facing_direction: "right",
+ },
+ {
+ type: "schematic_port",
+ schematic_port_id: "schematic_port_2",
+ schematic_component_id: "schematic_component_1",
+ center: { x: -0.5, y: 0 },
+ source_port_id: "source_port_2",
+ facing_direction: "left",
+ },
+ {
+ type: "schematic_port",
+ schematic_port_id: "schematic_port_3",
+ schematic_component_id: "schematic_component_1",
+ center: { x: 0.5, y: 0 },
+ source_port_id: "source_port_3",
+ facing_direction: "right",
+ },
+ {
+ type: "schematic_port",
+ schematic_port_id: "schematic_port_4",
+ schematic_component_id: "schematic_component_2",
+ center: { x: -0.5, y: 0 },
+ source_port_id: "source_port_4",
+ facing_direction: "left",
+ },
+ {
+ type: "schematic_port",
+ schematic_port_id: "schematic_port_5",
+ schematic_component_id: "schematic_component_2",
+ center: { x: 0.5, y: 0 },
+ source_port_id: "source_port_5",
+ facing_direction: "right",
+ },
+ {
+ type: "pcb_component",
+ pcb_component_id: "pcb_component_0",
+ center: { x: -5, y: 0 },
+ width: 1.5999999999999996,
+ height: 0.6000000000000001,
+ layer: "top",
+ rotation: 0,
+ source_component_id: "source_component_0",
+ },
+ {
+ type: "pcb_component",
+ pcb_component_id: "pcb_component_1",
+ center: { x: 5, y: 0 },
+ width: 1.5999999999999996,
+ height: 0.6000000000000001,
+ layer: "top",
+ rotation: 0,
+ source_component_id: "source_component_1",
+ },
+ {
+ type: "pcb_component",
+ pcb_component_id: "pcb_component_2",
+ center: { x: 0, y: 0 },
+ width: 4.3,
+ height: 1.5,
+ layer: "top",
+ rotation: 0,
+ source_component_id: "source_component_2",
+ },
+ {
+ type: "pcb_board",
+ pcb_board_id: "pcb_board_0",
+ center: { x: 0, y: 0 },
+ width: 12,
+ height: 10,
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pcb_smtpad_0",
+ pcb_component_id: "pcb_component_0",
+ pcb_port_id: "pcb_port_0",
+ layer: "bottom",
+ shape: "rect",
+ width: 0.6000000000000001,
+ height: 0.6000000000000001,
+ port_hints: ["1", "left"],
+ x: -5.5,
+ y: 0,
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pcb_smtpad_1",
+ pcb_component_id: "pcb_component_0",
+ pcb_port_id: "pcb_port_1",
+ layer: "bottom",
+ shape: "rect",
+ width: 0.6000000000000001,
+ height: 0.6000000000000001,
+ port_hints: ["2", "right"],
+ x: -4.5,
+ y: 0,
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pcb_smtpad_2",
+ pcb_component_id: "pcb_component_1",
+ pcb_port_id: "pcb_port_2",
+ layer: "bottom",
+ shape: "rect",
+ width: 0.6000000000000001,
+ height: 0.6000000000000001,
+ port_hints: ["1", "left"],
+ x: 4.5,
+ y: 0,
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pcb_smtpad_3",
+ pcb_component_id: "pcb_component_1",
+ pcb_port_id: "pcb_port_3",
+ layer: "bottom",
+ shape: "rect",
+ width: 0.6000000000000001,
+ height: 0.6000000000000001,
+ port_hints: ["2", "right"],
+ x: 5.5,
+ y: 0,
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pcb_smtpad_4",
+ pcb_component_id: "pcb_component_2",
+ pcb_port_id: "pcb_port_4",
+ layer: "top",
+ shape: "rect",
+ width: 1.5,
+ height: 1.5,
+ port_hints: ["1", "left"],
+ x: -1.4,
+ y: 0,
+ },
+ {
+ type: "pcb_smtpad",
+ pcb_smtpad_id: "pcb_smtpad_5",
+ pcb_component_id: "pcb_component_2",
+ pcb_port_id: "pcb_port_5",
+ layer: "top",
+ shape: "rect",
+ width: 1.5,
+ height: 1.5,
+ port_hints: ["2", "right"],
+ x: 1.4,
+ y: 0,
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pcb_port_0",
+ pcb_component_id: "pcb_component_0",
+ layers: ["bottom"],
+ x: -5.5,
+ y: 0,
+ source_port_id: "source_port_0",
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pcb_port_1",
+ pcb_component_id: "pcb_component_0",
+ layers: ["bottom"],
+ x: -4.5,
+ y: 0,
+ source_port_id: "source_port_1",
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pcb_port_2",
+ pcb_component_id: "pcb_component_1",
+ layers: ["bottom"],
+ x: 4.5,
+ y: 0,
+ source_port_id: "source_port_2",
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pcb_port_3",
+ pcb_component_id: "pcb_component_1",
+ layers: ["bottom"],
+ x: 5.5,
+ y: 0,
+ source_port_id: "source_port_3",
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pcb_port_4",
+ pcb_component_id: "pcb_component_2",
+ layers: ["top"],
+ x: -1.4,
+ y: 0,
+ source_port_id: "source_port_4",
+ },
+ {
+ type: "pcb_port",
+ pcb_port_id: "pcb_port_5",
+ pcb_component_id: "pcb_component_2",
+ layers: ["top"],
+ x: 1.4,
+ y: 0,
+ source_port_id: "source_port_5",
+ },
+ {
+ type: "pcb_trace_hint",
+ pcb_trace_hint_id: "pcb_trace_hint_0",
+ pcb_component_id: null,
+ pcb_port_id: "pcb_port_1",
+ route: [{ x: -3, y: 0, via: true }],
+ },
+ {
+ type: "pcb_trace_hint",
+ pcb_trace_hint_id: "pcb_trace_hint_1",
+ pcb_component_id: null,
+ pcb_port_id: "pcb_port_2",
+ route: [{ x: 3, y: 0, via: true }],
+ },
+]
+
+test("ijump-astar: multilayer trace", () => {
+ const input = getSimpleRouteJson(soup)
+
+ const autorouter = new IJumpAutorouter({
+ input,
+ })
+
+ const traces = autorouter.solveAndMapToTraces()
+
+ Bun.write("test.json", JSON.stringify(soup.concat(traces)))
+
+ expect(circuitJsonToPcbSvg(soup.concat(traces))).toMatchSvgSnapshot(
+ import.meta.path,
+ )
+})
diff --git a/algos/infinite-grid-ijump-astar/v1/lib/getObstaclesFromTrace.ts b/algos/infinite-grid-ijump-astar/v1/lib/getObstaclesFromTrace.ts
index c212394..5075ddf 100644
--- a/algos/infinite-grid-ijump-astar/v1/lib/getObstaclesFromTrace.ts
+++ b/algos/infinite-grid-ijump-astar/v1/lib/getObstaclesFromTrace.ts
@@ -22,6 +22,7 @@ export const getObstaclesFromTrace = (
const obstacle: Obstacle = {
type: "rect",
+ layers: [start.layer],
center: {
x: (start.x + end.x) / 2,
y: (start.y + end.y) / 2,
diff --git a/algos/infinite-grid-ijump-astar/v2/lib/GeneralizedAstar.ts b/algos/infinite-grid-ijump-astar/v2/lib/GeneralizedAstar.ts
index 12083eb..1cbdbad 100644
--- a/algos/infinite-grid-ijump-astar/v2/lib/GeneralizedAstar.ts
+++ b/algos/infinite-grid-ijump-astar/v2/lib/GeneralizedAstar.ts
@@ -15,9 +15,13 @@ import { ObstacleList } from "./ObstacleList"
const debug = Debug("autorouting-dataset:astar")
+export interface PointWithLayer extends Point {
+ layer: string
+}
+
export type ConnectionSolveResult =
| { solved: false; connectionName: string }
- | { solved: true; connectionName: string; route: Point[] }
+ | { solved: true; connectionName: string; route: PointWithLayer[] }
export class GeneralizedAstarAutorouter {
openSet: Node[] = []
@@ -185,10 +189,15 @@ export class GeneralizedAstarAutorouter {
const { solved, current } = this.solveOneStep()
if (solved) {
- const route: Point[] = []
+ const route: PointWithLayer[] = []
let node: Node | null = current
while (node) {
- route.unshift({ x: node.x, y: node.y })
+ route.unshift({
+ x: node.x,
+ y: node.y,
+ // TODO: this layer should be included as part of the node
+ layer: pointsToConnect[0].layer,
+ })
node = node.parent
}
@@ -221,10 +230,15 @@ export class GeneralizedAstarAutorouter {
const obstaclesFromTraces: Obstacle[] = []
this.debugTraceCount = 0
for (const connection of this.input.connections) {
+ const dominantLayer = connection.pointsToConnect[0].layer ?? "top"
this.debugTraceCount += 1
this.obstacles = new ObstacleList(
this.allObstacles
.filter((obstacle) => !obstacle.connectedTo.includes(connection.name))
+ // TODO obstacles on different layers should be filtered inside
+ // the algorithm, not for the entire connection, this is a hack in
+ // relation to https://github.com/tscircuit/tscircuit/issues/432
+ .filter((obstacle) => obstacle.layers.includes(dominantLayer))
.concat(obstaclesFromTraces),
)
const result = this.solveConnection(connection)
@@ -237,7 +251,14 @@ export class GeneralizedAstarAutorouter {
if (result.solved) {
solutions.push(result)
obstaclesFromTraces.push(
- ...getObstaclesFromRoute(result.route, connection.name),
+ ...getObstaclesFromRoute(
+ result.route.map((p) => ({
+ x: p.x,
+ y: p.y,
+ layer: dominantLayer,
+ })),
+ connection.name,
+ ),
)
}
}
@@ -259,7 +280,7 @@ export class GeneralizedAstarAutorouter {
x: point.x,
y: point.y,
width: 0.1, // TODO use configurable width
- layer: "top", // Default layer, adjust as needed
+ layer: point.layer,
})),
},
]
diff --git a/bun.lockb b/bun.lockb
index 735de2d..7a58d1f 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/bunfig.toml b/bunfig.toml
new file mode 100644
index 0000000..b13c929
--- /dev/null
+++ b/bunfig.toml
@@ -0,0 +1,2 @@
+[test]
+preload = ["./module/tests/fixtures/preload.ts"]
\ No newline at end of file
diff --git a/module/lib/generators/index.ts b/module/lib/generators/index.ts
index 5e1ddd0..c5adeee 100644
--- a/module/lib/generators/index.ts
+++ b/module/lib/generators/index.ts
@@ -1,5 +1,6 @@
import { getDistantSingleTraceProblemGenerator } from "./distant-single-trace"
import { getSingleTraceProblemGenerator } from "./single-trace"
+import { getSingleTraceMultilayerProblemGenerator } from "./single-trace-multilayer"
import { getTracesProblemGenerator } from "./traces"
import type { ProblemGenerator, ProblemType } from "./types"
@@ -12,6 +13,8 @@ export const getDatasetGenerator = (
return getTracesProblemGenerator()
} else if (problemType === "distant-single-trace") {
return getDistantSingleTraceProblemGenerator()
+ } else if (problemType === "single-trace-multilayer") {
+ return getSingleTraceMultilayerProblemGenerator()
}
throw new Error(
`Generator for "${problemType}" not found, may not be implemented`,
diff --git a/module/lib/generators/single-trace-multilayer/SingleTraceMultilayerCircuit.tsx b/module/lib/generators/single-trace-multilayer/SingleTraceMultilayerCircuit.tsx
new file mode 100644
index 0000000..777aa2b
--- /dev/null
+++ b/module/lib/generators/single-trace-multilayer/SingleTraceMultilayerCircuit.tsx
@@ -0,0 +1,65 @@
+import { getRandomFootprint } from "../../generator-utils/getRandomFootprint"
+import { rand } from "../../generator-utils/rand"
+
+export type SingleTrace2FootprintsProps = {
+ seed: number
+ distance?: number
+}
+
+const seed1Footprint = {
+ footprint: "0402",
+ pinCount: 2,
+ footprintType: "passive",
+}
+
+export const SingleTrace2Footprints = ({
+ seed,
+ distance = 10,
+}: SingleTrace2FootprintsProps) => {
+ const rotation = rand(seed, "rotation").range(0, Math.PI * 2)
+ const aFoot =
+ seed === 1 ? seed1Footprint : getRandomFootprint([seed, "AFootprint"])
+ const bFoot =
+ seed === 1 ? seed1Footprint : getRandomFootprint([seed, "BFootprint"])
+ const A = () => {
+ return
+ }
+ const B = () => {
+ const pcbX = Math.cos(rotation) * distance
+ const pcbY = Math.sin(rotation) * distance
+ return (
+
+ )
+ }
+
+ return (
+
+
+
+ port.${rand(seed, "aFrom").int(1, aFoot.pinCount)}`}
+ to={`.B > port.${rand(seed, "bFrom").int(1, bFoot.pinCount)}`}
+ />
+
+ )
+}
+
+export const SingleTraceMultilayerCircuit = ({
+ seed,
+ distance,
+}: SingleTrace2FootprintsProps) => {
+ // TODO randomly select a layout configuration using rand(seed).int(0, NUM_CONFIGURATIONS)
+ return
+}
diff --git a/module/lib/generators/single-trace-multilayer/index.tsx b/module/lib/generators/single-trace-multilayer/index.tsx
new file mode 100644
index 0000000..ad9789a
--- /dev/null
+++ b/module/lib/generators/single-trace-multilayer/index.tsx
@@ -0,0 +1,22 @@
+import type { AnySoupElement } from "@tscircuit/soup"
+import { renderCircuitToSoup } from "../../generator-utils/renderCircuitToSoup"
+import type { ProblemGenerator } from "../types"
+import { SingleTraceMultilayerCircuit } from "./SingleTraceMultilayerCircuit"
+import { replaceTracesWithErrors } from "../../generator-utils/replaceTracesWithErrors"
+import { withCheckRegenerate } from "../utils/with-check-regenerate"
+
+export const getSingleTraceMultilayerProblemGenerator =
+ (): ProblemGenerator => {
+ const generateSingleTraceMultilayerProblem: ProblemGenerator["getExample"] =
+ async ({ seed }): Promise => {
+ return replaceTracesWithErrors(
+ await renderCircuitToSoup(
+ ,
+ ),
+ )
+ }
+
+ return {
+ getExample: withCheckRegenerate(generateSingleTraceMultilayerProblem),
+ }
+ }
diff --git a/module/lib/generators/types.ts b/module/lib/generators/types.ts
index 98a8e4b..5034362 100644
--- a/module/lib/generators/types.ts
+++ b/module/lib/generators/types.ts
@@ -1,6 +1,10 @@
import type { AnySoupElement } from "@tscircuit/soup"
-export type ProblemType = "single-trace" | "traces" | "distant-single-trace"
+export type ProblemType =
+ | "single-trace"
+ | "traces"
+ | "distant-single-trace"
+ | "single-trace-multilayer"
export type ProblemGenerator = {
getExample: (params: { seed: number }) => Promise
diff --git a/module/lib/server/available-datasets.ts b/module/lib/server/available-datasets.ts
index 1de4514..d91b35c 100644
--- a/module/lib/server/available-datasets.ts
+++ b/module/lib/server/available-datasets.ts
@@ -2,4 +2,5 @@ export const AVAILABLE_DATASETS = [
"single-trace",
"traces",
"distant-single-trace",
+ "single-trace-multilayer",
]
diff --git a/module/lib/solver-utils/SimpleRouteJson.ts b/module/lib/solver-utils/SimpleRouteJson.ts
index 043472f..2670bee 100644
--- a/module/lib/solver-utils/SimpleRouteJson.ts
+++ b/module/lib/solver-utils/SimpleRouteJson.ts
@@ -2,7 +2,7 @@ import type { Obstacle } from "../types"
export interface SimpleRouteConnection {
name: string
- pointsToConnect: Array<{ x: number; y: number }>
+ pointsToConnect: Array<{ x: number; y: number; layer: string }>
}
export interface SimpleRouteJson {
diff --git a/module/lib/solver-utils/getObstaclesFromCircuitJson.ts b/module/lib/solver-utils/getObstaclesFromCircuitJson.ts
index 41564ba..11ea43c 100644
--- a/module/lib/solver-utils/getObstaclesFromCircuitJson.ts
+++ b/module/lib/solver-utils/getObstaclesFromCircuitJson.ts
@@ -2,6 +2,8 @@ import type { AnySoupElement } from "@tscircuit/soup"
import type { Obstacle } from "../types"
import { getObstaclesFromRoute } from "./getObstaclesFromRoute"
+const EVERY_LAYER = ["top", "inner1", "inner2", "bottom"]
+
export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
const obstacles: Obstacle[] = []
for (const element of soup) {
@@ -10,6 +12,7 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
obstacles.push({
// @ts-ignore
type: "oval",
+ layers: [element.layer],
center: {
x: element.x,
y: element.y,
@@ -21,6 +24,7 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
} else if (element.shape === "rect") {
obstacles.push({
type: "rect",
+ layers: [element.layer],
center: {
x: element.x,
y: element.y,
@@ -35,6 +39,7 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
obstacles.push({
// @ts-ignore
type: "oval",
+ layers: element.layers,
center: {
x: element.center.x,
y: element.center.y,
@@ -46,9 +51,10 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
} else if (element.shape === "rect") {
obstacles.push({
type: "rect",
+ layers: element.layers,
center: {
- x: element.x,
- y: element.y,
+ x: element.center.x,
+ y: element.center.y,
},
width: element.width,
height: element.height,
@@ -71,6 +77,7 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
} else if (element.hole_shape === "square") {
obstacles.push({
type: "rect",
+ layers: EVERY_LAYER,
center: {
x: element.x,
y: element.y,
@@ -82,6 +89,7 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
} else if (element.hole_shape === "round") {
obstacles.push({
type: "rect",
+ layers: EVERY_LAYER,
center: {
x: element.x,
y: element.y,
@@ -96,6 +104,7 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
obstacles.push({
// @ts-ignore
type: "oval",
+ layers: EVERY_LAYER,
center: {
x: element.x,
y: element.y,
@@ -108,6 +117,7 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
obstacles.push({
// @ts-ignore
type: "oval",
+ layers: EVERY_LAYER,
center: {
x: element.x,
y: element.y,
@@ -119,7 +129,11 @@ export const getObstaclesFromCircuitJson = (soup: AnySoupElement[]) => {
}
} else if (element.type === "pcb_trace") {
const traceObstacles = getObstaclesFromRoute(
- element.route,
+ element.route.map((rp) => ({
+ x: rp.x,
+ y: rp.y,
+ layer: "layer" in rp ? rp.layer : rp.from_layer,
+ })),
element.source_trace_id!,
)
obstacles.push(...traceObstacles)
diff --git a/module/lib/solver-utils/getObstaclesFromRoute.ts b/module/lib/solver-utils/getObstaclesFromRoute.ts
index 8901e07..71694ca 100644
--- a/module/lib/solver-utils/getObstaclesFromRoute.ts
+++ b/module/lib/solver-utils/getObstaclesFromRoute.ts
@@ -3,6 +3,7 @@ import type { Obstacle } from "autorouting-dataset/lib/types"
interface Point {
x: number
y: number
+ layer: string
}
export const getObstaclesFromRoute = (
@@ -24,6 +25,7 @@ export const getObstaclesFromRoute = (
const obstacle: Obstacle = {
type: "rect",
+ layers: [start.layer],
center: {
x: (start.x + end.x) / 2,
y: (start.y + end.y) / 2,
diff --git a/module/lib/solver-utils/getSimpleRouteJson.ts b/module/lib/solver-utils/getSimpleRouteJson.ts
index 593f5a3..173a47a 100644
--- a/module/lib/solver-utils/getSimpleRouteJson.ts
+++ b/module/lib/solver-utils/getSimpleRouteJson.ts
@@ -30,6 +30,7 @@ export const getSimpleRouteJson = (soup: AnySoupElement[]): SimpleRouteJson => {
return {
x: pcb_port.x,
y: pcb_port.y,
+ layer: pcb_port.layers?.[0] ?? "top",
}
}),
}
diff --git a/module/lib/types.ts b/module/lib/types.ts
index 6fefc37..c72c1b6 100644
--- a/module/lib/types.ts
+++ b/module/lib/types.ts
@@ -18,6 +18,7 @@ export interface Point {
export type Obstacle = {
// TODO include ovals
type: "rect" // NOTE: most datasets do not contain ovals
+ layers: string[]
center: { x: number; y: number }
width: number
height: number
diff --git a/module/tests/fixtures/preload.ts b/module/tests/fixtures/preload.ts
new file mode 100644
index 0000000..ee67a60
--- /dev/null
+++ b/module/tests/fixtures/preload.ts
@@ -0,0 +1 @@
+import "bun-match-svg"
diff --git a/module/tests/getObstaclesFromCircuitJson.test.ts b/module/tests/getObstaclesFromCircuitJson.test.ts
index 8f7d552..9f0f025 100644
--- a/module/tests/getObstaclesFromCircuitJson.test.ts
+++ b/module/tests/getObstaclesFromCircuitJson.test.ts
@@ -26,12 +26,14 @@ test("pcb_trace becomes an obstacle correctly", () => {
center: { x: 5, y: 0 },
width: 10,
height: 0.1,
+ layers: ["top"],
connectedTo: ["trace1"],
})
// Check the second obstacle (vertical trace)
expect(obstacles[1]).toEqual({
type: "rect",
+ layers: ["top"],
center: { x: 10, y: 5 },
width: 0.1,
height: 10,
diff --git a/package.json b/package.json
index 12e9b44..cf8db67 100644
--- a/package.json
+++ b/package.json
@@ -18,8 +18,8 @@
"@tscircuit/builder": "1.11.2",
"@tscircuit/pcb-viewer": "1.4.5",
"@tscircuit/props": "^0.0.26",
- "@tscircuit/soup": "^0.0.66",
- "@tscircuit/soup-util": "^0.0.21",
+ "@tscircuit/soup": "^0.0.68",
+ "@tscircuit/soup-util": "^0.0.23",
"@types/bun": "latest",
"@types/d3-delaunay": "^6.0.4",
"@types/debug": "^4.1.12",
@@ -29,6 +29,8 @@
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
+ "bun-match-svg": "^0.0.3",
+ "circuit-to-svg": "^0.0.22",
"concurrently": "^8.2.2",
"d3-delaunay": "^6.0.4",
"debug": "^4.3.6",