Skip to content

Commit

Permalink
Merge pull request #69 from tscircuit/merge-dsn-session-parity
Browse files Browse the repository at this point in the history
fix merge dsn session to have SVG parity
  • Loading branch information
seveibar authored Dec 17, 2024
2 parents 7bc8995 + e868ea8 commit afac287
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { DsnPcb, DsnSession, Wire } from "../types"
import { su } from "@tscircuit/soup-util"
import { convertCircuitJsonToDsnJson } from "./convert-circuit-json-to-dsn-json"
import { applyToPoint, scale } from "transformation-matrix"
import { processPcbTraces } from "./process-pcb-traces"

export function convertCircuitJsonToDsnSession(
dsnPcb: DsnPcb,
Expand All @@ -25,57 +26,16 @@ export function convertCircuitJsonToDsnSession(
resolution: dsnPcb.resolution,
parser: dsnPcb.parser,
library_out: {
images: [],
padstacks: [],
},
network_out: {
nets: pcb_traces.map((trace) => {
const source_trace = source_traces.find(
(st) => st.source_trace_id === trace.source_trace_id,
)
const source_net =
source_trace &&
nets.find((n) =>
source_trace.connected_source_net_ids.includes(n.source_net_id),
)
const net_name = source_net?.name || trace.source_trace_id

// TODO only supports single layer traces
const traceLayer =
"layer" in trace.route[0] && trace.route[0].layer === "bottom"
? "bottom"
: "top"

const traceWidth =
"width" in trace.route[0] ? trace.route[0].width : 0.16

return {
name: net_name!,
wires: [
{
path: {
layer: traceLayer === "bottom" ? "B.Cu" : "F.Cu",
width: traceWidth * 1000,
coordinates: trace.route
.filter(
(rp): rp is PcbTraceRoutePointWire =>
rp.route_type === "wire",
)
.map((rp) =>
// Circuit JSON space to the SES space
applyToPoint(transformMmToSesUnit, {
x: rp.x,
y: rp.y,
}),
)
.flatMap((trp) => [trp.x, trp.y]),
},
},
],
}
}),
nets: [],
},
},
}

processPcbTraces(circuitJson, session)

return session
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { DsnPcb, DsnSession, Wire } from "lib/dsn-pcb/types"

export interface DsnTraceOperationsWrapper {
getNextNetId(): string
addWire(wire: Wire): void
getLibrary(): DsnPcb["library"]
getStructure(): DsnPcb["structure"] | null
}

/**
* This operations wrapper allows you to operate on either DSN PCB or DSN
* Session objects, increasing code reusability when working with traces.
*
* Please add more methods to this as you need them!
*/
export const getDsnTraceOperationsWrapper = (
dsnObj: DsnPcb | DsnSession,
): DsnTraceOperationsWrapper => {
if (dsnObj.is_dsn_pcb) {
return {
getNextNetId: () => `Net-${dsnObj.network.nets.length + 1}`,
addWire: (wire: Wire) => {
dsnObj.wiring.wires.push(wire as any)
},
getStructure: () => dsnObj.structure,
getLibrary: () => dsnObj.library,
}
}

if (dsnObj.is_dsn_session) {
return {
getNextNetId: () => `Net-${dsnObj.routes.network_out.nets.length + 1}`,
getLibrary: () => dsnObj.routes.library_out!,
getStructure: () => null,
addWire: (wire: Wire) => {
let net = dsnObj.routes.network_out.nets.find(
(net) => net.name === wire.net,
)
if (!net) {
net = {
name: wire.net!,
wires: [],
}
dsnObj.routes.network_out.nets.push(net)
}
net.wires.push(wire)
},
}
}

throw new Error("Invalid DSN object")
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import type { DsnPcb, Padstack } from "lib/dsn-pcb/types"
import type { DsnTraceOperationsWrapper } from "./DsnTraceOperationsWrapper"

export function findOrCreateViaPadstack(
pcb: DsnPcb,
pcb: DsnTraceOperationsWrapper,
outerDiameter: number,
holeDiameter: number,
): string {
const viaName = `Via[0-1]_${outerDiameter}:${holeDiameter}_um`

const library = pcb.getLibrary()

// Check if padstack already exists
const existingPadstack = pcb.library.padstacks.find((p) => p.name === viaName)
const existingPadstack = library.padstacks.find((p) => p.name === viaName)

if (existingPadstack) {
return viaName
Expand Down Expand Up @@ -36,6 +39,6 @@ export function findOrCreateViaPadstack(
},
}

pcb.library.padstacks.push(viaPadstack)
library.padstacks.push(viaPadstack)
return viaName
}
69 changes: 49 additions & 20 deletions lib/dsn-pcb/circuit-json-to-dsn-json/process-pcb-traces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import type {
import type { DsnPcb, DsnSession } from "../../types"
import Debug from "debug"
import { findOrCreateViaPadstack } from "./findOrCreateViaPadstack"
import { getDsnTraceOperationsWrapper } from "./DsnTraceOperationsWrapper"
import { su } from "@tscircuit/soup-util"

const debug = Debug("dsn-converter:process-pcb-traces")
const debug = Debug("dsn-converter:processPcbTraces")

const DEFAULT_VIA_DIAMETER = 600 // μm
const DEFAULT_VIA_HOLE = 300 // μm
Expand Down Expand Up @@ -41,14 +43,29 @@ function createWire(opts: {

export function processPcbTraces(
circuitElements: AnyCircuitElement[],
pcb: DsnPcb,
pcb: DsnPcb | DsnSession,
) {
const dsnWrapper = getDsnTraceOperationsWrapper(pcb)
const CJ_TO_DSN_SCALE = pcb.is_dsn_pcb ? 1000 : 10000

for (const element of circuitElements) {
if (element.type === "pcb_trace") {
const pcbTrace = element
const source_trace = su(circuitElements).source_trace.getWhere({
source_trace_id: pcbTrace.source_trace_id,
})
const source_net =
source_trace &&
su(circuitElements)
.source_net.list()
.find((n) =>
source_trace.connected_source_net_ids.includes(n.source_net_id),
)
debug("PCB TRACE\n----------\n", pcbTrace)
const netName =
pcbTrace.source_trace_id || `Net-${pcb.network.nets.length + 1}`
source_net?.name ||
pcbTrace.source_trace_id ||
dsnWrapper.getNextNetId()

let currentLayer = ""
let currentWire: Wire | null = null
Expand All @@ -71,36 +88,39 @@ export function processPcbTraces(
netName,
})

pcb.wiring.wires.push(currentWire)
dsnWrapper.addWire(currentWire)
currentLayer = point.layer
}

if (currentWire && !hasLayerChanged) {
// Add coordinates to current wire
currentWire.path.coordinates.push(point.x * 1000)
currentWire.path.coordinates.push(point.y * 1000)
currentWire.path.coordinates.push(point.x * CJ_TO_DSN_SCALE)
currentWire.path.coordinates.push(point.y * CJ_TO_DSN_SCALE)
continue
}

if (hasLayerChanged) {
const prevPoint = pcbTrace.route[i - 1]
const viaPadstackName = findOrCreateViaPadstack(
pcb,
dsnWrapper,
DEFAULT_VIA_DIAMETER,
DEFAULT_VIA_HOLE,
)

// Add via reference to structure if not already there
if (!pcb.structure.via) {
pcb.structure.via = viaPadstackName
if (dsnWrapper.getStructure() && !dsnWrapper.getStructure()?.via) {
dsnWrapper.getStructure()!.via = viaPadstackName
}

// Create wire segment for via placement
pcb.wiring.wires.push({
dsnWrapper.addWire({
path: {
layer: currentLayer === "top" ? "F.Cu" : "B.Cu",
width: DEFAULT_VIA_DIAMETER,
coordinates: [prevPoint.x * 1000, prevPoint.y * 1000],
coordinates: [
prevPoint.x * CJ_TO_DSN_SCALE,
prevPoint.y * CJ_TO_DSN_SCALE,
],
},
net: netName,
type: "via",
Expand All @@ -114,42 +134,51 @@ export function processPcbTraces(

// End current wire
if (currentWire) {
currentWire.path.coordinates.push(point.x * 1000)
currentWire.path.coordinates.push(point.y * 1000)
currentWire.path.coordinates.push(point.x * CJ_TO_DSN_SCALE)
currentWire.path.coordinates.push(point.y * CJ_TO_DSN_SCALE)
currentWire = null
}

// Handle explicit via points
const viaPadstackName = findOrCreateViaPadstack(
pcb,
dsnWrapper,
DEFAULT_VIA_DIAMETER,
DEFAULT_VIA_HOLE,
)

debug("VIA PADSTACK NAME:", viaPadstackName)

// Add via reference to structure if not already there
if (!pcb.structure.via) {
pcb.structure.via = viaPadstackName
if (dsnWrapper.getStructure() && !dsnWrapper.getStructure()?.via) {
dsnWrapper.getStructure()!.via = viaPadstackName
}

// Create wire segment for via placement
pcb.wiring.wires.push({
dsnWrapper.addWire({
path: {
layer: point.from_layer === "top" ? "F.Cu" : "B.Cu",
width: DEFAULT_VIA_DIAMETER,
coordinates: [point.x * 1000, point.y * 1000],
coordinates: [
point.x * CJ_TO_DSN_SCALE,
point.y * CJ_TO_DSN_SCALE,
],
},
net: netName,
type: "via",
})
debug("WIRING", pcb.wiring)

currentLayer = point.to_layer
currentWire = null // Start fresh wire after via
}
}
}
}
debug("PCB WIRING AT END", pcb.wiring)
debug(
"PCB WIRING/NETWORK_OUT AT END",
JSON.stringify(
pcb.is_dsn_pcb ? pcb.wiring : pcb.routes.network_out.nets,
null,
2,
),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export function mergeDsnSessionIntoDsnPcb(
coordinates: wire.path.coordinates.map((c) => c / 10),
},
net: sessionNet.name,
type: "route",
type: wire.type ?? "route",
})
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,7 @@ function processSessionNode(ast: ASTNode): DsnSession {
)
if (libraryNode) {
session.routes.library_out = {
images: [],
padstacks: libraryNode
.children!.filter(
(child) =>
Expand Down
1 change: 1 addition & 0 deletions lib/dsn-pcb/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ export interface DsnSession {
resolution: Resolution
parser: Parser
library_out?: {
images: Image[]
padstacks: Padstack[]
}
network_out: {
Expand Down
2 changes: 1 addition & 1 deletion tests/dsn-pcb/merge-dsn-session-with-conversion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,5 +116,5 @@ test("merge-dsn-session-with-conversion", async () => {
)
// TODO requires fix inside convertCircuitJsonToDsnSession, currently the vias
// aren't converted properly- reference or adapt the code in processPcbTraces
// expect(looksSameResult.equal).toBe(true) // Should be identical after merge
expect(looksSameResult.equal).toBe(true) // Should be identical after merge
})

0 comments on commit afac287

Please sign in to comment.