Skip to content

Commit 9b28f03

Browse files
authored
refactor the file names and the pipeline solver (#14)
* refactor the file names and the pipeline solver * fix import name
1 parent d97751f commit 9b28f03

File tree

5 files changed

+246
-15
lines changed

5 files changed

+246
-15
lines changed
Lines changed: 191 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { BaseSolver } from "@tscircuit/solver-utils"
22
import type { GraphicsObject } from "graphics-debug"
3-
import type { PcbTrace, PcbVia, LayerRef } from "circuit-json"
3+
import type {
4+
PcbTrace,
5+
PcbVia,
6+
LayerRef,
7+
PcbSmtPad,
8+
PcbPlatedHole,
9+
} from "circuit-json"
410
import { hslToHex } from "./utils/hslToHex"
511

612
/**
@@ -14,6 +20,8 @@ export type ColorMode = "layer" | "trace"
1420
export interface TraceViewerInput {
1521
traces: PcbTrace[]
1622
vias: PcbVia[]
23+
smtpads?: PcbSmtPad[]
24+
platedHoles?: PcbPlatedHole[]
1725
boardBounds?: {
1826
minX: number
1927
minY: number
@@ -30,6 +38,8 @@ export interface TraceViewerOutput {
3038
stats: {
3139
traceCount: number
3240
viaCount: number
41+
smtpadCount: number
42+
platedHoleCount: number
3343
topLayerSegments: number
3444
bottomLayerSegments: number
3545
totalRoutePoints: number
@@ -80,6 +90,26 @@ export class TraceViewer extends BaseSolver {
8090
hole_diameter: number
8191
}> = []
8292

93+
// Scaled SMT pads for visualization
94+
private scaledSmtpads: Array<{
95+
x: number
96+
y: number
97+
shape: "rect" | "circle" | "pill" | "rotated_rect" | "polygon" | "rotated_pill"
98+
width?: number
99+
height?: number
100+
radius?: number
101+
layer: LayerRef
102+
}> = []
103+
104+
// Scaled plated holes for visualization
105+
private scaledPlatedHoles: Array<{
106+
x: number
107+
y: number
108+
outer_diameter: number
109+
hole_diameter: number
110+
shape: "circle" | "oval" | "pill"
111+
}> = []
112+
83113
// Map of trace IDs to their assigned colors
84114
private traceColors: Map<string, string> = new Map()
85115

@@ -129,6 +159,12 @@ export class TraceViewer extends BaseSolver {
129159
// Scale vias for visualization
130160
this.scaleVias()
131161

162+
// Scale SMT pads for visualization
163+
this.scaleSmtpads()
164+
165+
// Scale plated holes for visualization
166+
this.scalePlatedHoles()
167+
132168
// Assign colors to each unique trace ID
133169
this.assignTraceColors()
134170

@@ -170,6 +206,68 @@ export class TraceViewer extends BaseSolver {
170206
}
171207
}
172208

209+
/**
210+
* Scale SMT pads for visualization
211+
*/
212+
private scaleSmtpads(): void {
213+
const smtpads = this.input.smtpads || []
214+
for (const pad of smtpads) {
215+
// Skip polygon pads for now as they don't have simple x/y coordinates
216+
if (pad.shape === "polygon") {
217+
continue
218+
}
219+
220+
const scaledPad: (typeof this.scaledSmtpads)[0] = {
221+
x: pad.x * this.scale,
222+
y: pad.y * this.scale,
223+
shape: pad.shape,
224+
layer: pad.layer,
225+
}
226+
227+
if (pad.shape === "rect" || pad.shape === "rotated_rect") {
228+
scaledPad.width = (pad.width ?? 0) * this.scale
229+
scaledPad.height = (pad.height ?? 0) * this.scale
230+
} else if (pad.shape === "circle") {
231+
scaledPad.radius = (pad.radius ?? 0) * this.scale
232+
} else if (pad.shape === "pill" || pad.shape === "rotated_pill") {
233+
scaledPad.width = (pad.width ?? 0) * this.scale
234+
scaledPad.height = (pad.height ?? 0) * this.scale
235+
scaledPad.radius = (pad.radius ?? 0) * this.scale
236+
}
237+
238+
this.scaledSmtpads.push(scaledPad)
239+
}
240+
}
241+
242+
/**
243+
* Scale plated holes for visualization
244+
*/
245+
private scalePlatedHoles(): void {
246+
const platedHoles = this.input.platedHoles || []
247+
for (const hole of platedHoles) {
248+
// Handle different plated hole shapes
249+
if (hole.shape === "circle") {
250+
this.scaledPlatedHoles.push({
251+
x: hole.x * this.scale,
252+
y: hole.y * this.scale,
253+
outer_diameter: hole.outer_diameter * this.scale,
254+
hole_diameter: hole.hole_diameter * this.scale,
255+
shape: "circle",
256+
})
257+
} else if (hole.shape === "oval" || hole.shape === "pill") {
258+
// Oval and pill shaped holes use outer_width/height instead of diameter
259+
this.scaledPlatedHoles.push({
260+
x: hole.x * this.scale,
261+
y: hole.y * this.scale,
262+
outer_diameter: Math.max(hole.outer_width, hole.outer_height) * this.scale,
263+
hole_diameter: Math.max(hole.hole_width, hole.hole_height) * this.scale,
264+
shape: hole.shape,
265+
})
266+
}
267+
// Skip complex shapes like circular_hole_with_rect_pad, etc. for now
268+
}
269+
}
270+
173271
/**
174272
* Parse traces into segments for easier visualization
175273
* Applies scaling to all coordinates
@@ -225,7 +323,7 @@ export class TraceViewer extends BaseSolver {
225323
}
226324

227325
/**
228-
* Calculate board bounds from scaled trace segments and vias
326+
* Calculate board bounds from scaled trace segments, vias, pads, and plated holes
229327
*/
230328
private calculateBoardBounds(): void {
231329
let minX = Infinity
@@ -251,6 +349,25 @@ export class TraceViewer extends BaseSolver {
251349
maxY = Math.max(maxY, via.y)
252350
}
253351

352+
// Use already-scaled SMT pads
353+
for (const pad of this.scaledSmtpads) {
354+
const halfWidth = (pad.width ?? pad.radius ?? 0) / 2
355+
const halfHeight = (pad.height ?? pad.radius ?? 0) / 2
356+
minX = Math.min(minX, pad.x - halfWidth)
357+
minY = Math.min(minY, pad.y - halfHeight)
358+
maxX = Math.max(maxX, pad.x + halfWidth)
359+
maxY = Math.max(maxY, pad.y + halfHeight)
360+
}
361+
362+
// Use already-scaled plated holes
363+
for (const hole of this.scaledPlatedHoles) {
364+
const halfDiameter = hole.outer_diameter / 2
365+
minX = Math.min(minX, hole.x - halfDiameter)
366+
minY = Math.min(minY, hole.y - halfDiameter)
367+
maxX = Math.max(maxX, hole.x + halfDiameter)
368+
maxY = Math.max(maxY, hole.y + halfDiameter)
369+
}
370+
254371
if (minX === Infinity) {
255372
// No data, use defaults
256373
this.input.boardBounds = { minX: -50, minY: -50, maxX: 50, maxY: 50 }
@@ -392,9 +509,79 @@ export class TraceViewer extends BaseSolver {
392509
})
393510
}
394511

512+
// Draw SMT pads (always visible, underneath traces)
513+
for (const pad of this.scaledSmtpads) {
514+
const padColor = layerColors[pad.layer] || "#888"
515+
// Lighter version for pads (50% lighter)
516+
const lightPadColor = this.lightenColor(padColor, 0.5)
517+
518+
if (pad.shape === "circle") {
519+
graphics.circles!.push({
520+
center: { x: pad.x, y: pad.y },
521+
radius: pad.radius ?? 1,
522+
fill: lightPadColor,
523+
stroke: padColor,
524+
})
525+
} else if (
526+
pad.shape === "rect" ||
527+
pad.shape === "rotated_rect" ||
528+
pad.shape === "pill" ||
529+
pad.shape === "rotated_pill" ||
530+
pad.shape === "polygon"
531+
) {
532+
graphics.rects!.push({
533+
center: { x: pad.x, y: pad.y },
534+
width: pad.width ?? 1,
535+
height: pad.height ?? 1,
536+
fill: lightPadColor,
537+
stroke: padColor,
538+
})
539+
}
540+
}
541+
542+
// Draw plated holes (always visible)
543+
for (const hole of this.scaledPlatedHoles) {
544+
// Outer copper ring
545+
graphics.circles!.push({
546+
center: { x: hole.x, y: hole.y },
547+
radius: hole.outer_diameter / 2,
548+
fill: "#d4af37", // Gold color for plated holes
549+
stroke: "#b8960c",
550+
})
551+
552+
// Inner hole
553+
graphics.circles!.push({
554+
center: { x: hole.x, y: hole.y },
555+
radius: hole.hole_diameter / 2,
556+
fill: "#1a1a2e",
557+
stroke: "#333",
558+
})
559+
}
560+
395561
return graphics
396562
}
397563

564+
/**
565+
* Lighten a hex color by a factor
566+
*/
567+
private lightenColor(hexColor: string, factor: number): string {
568+
const r = parseInt(hexColor.slice(1, 3), 16)
569+
const g = parseInt(hexColor.slice(3, 5), 16)
570+
const b = parseInt(hexColor.slice(5, 7), 16)
571+
572+
const newR = Math.min(255, Math.round(r + (255 - r) * factor))
573+
.toString(16)
574+
.padStart(2, "0")
575+
const newG = Math.min(255, Math.round(g + (255 - g) * factor))
576+
.toString(16)
577+
.padStart(2, "0")
578+
const newB = Math.min(255, Math.round(b + (255 - b) * factor))
579+
.toString(16)
580+
.padStart(2, "0")
581+
582+
return `#${newR}${newG}${newB}`
583+
}
584+
398585
/**
399586
* Adjust color opacity by modifying the hex color
400587
*/
@@ -451,6 +638,8 @@ export class TraceViewer extends BaseSolver {
451638
stats: {
452639
traceCount: this.input.traces.length,
453640
viaCount: this.input.vias.length,
641+
smtpadCount: this.input.smtpads?.length ?? 0,
642+
platedHoleCount: this.input.platedHoles?.length ?? 0,
454643
topLayerSegments,
455644
bottomLayerSegments,
456645
totalRoutePoints,
Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,54 @@
11
import React, { useMemo, useState, useCallback } from "react"
22
import { GenericSolverDebugger } from "@tscircuit/solver-utils/react"
3-
import { TraceViewer, type ColorMode } from "../TraceViewer"
3+
import { TraceViewer, type ColorMode } from "../SesToCircuitJsonPipelineSolver"
44
import { convertSesToCircuitJson } from "../../lib/ses-to-pcb"
5-
import type { CircuitJson, PcbTrace, PcbVia } from "circuit-json"
5+
import { convertDsnToCircuitJson } from "../../lib/dsn-to-pcb"
6+
import type {
7+
PcbTrace,
8+
PcbVia,
9+
PcbSmtPad,
10+
PcbPlatedHole,
11+
} from "circuit-json"
612
// @ts-ignore
713
import MOTOR_DRIVER_SES from "./assets/output.ses?raw"
14+
// @ts-ignore
15+
import MOTOR_DRIVER_DSN from "./assets/motor_driver_input.dsn?raw"
816

917
export default function TraceViewer01Fixture() {
1018
const [colorMode, setColorMode] = useState<ColorMode>("layer")
1119
const [solverKey, setSolverKey] = useState(0)
1220

1321
const solver = useMemo(() => {
1422
try {
15-
const circuitJson = convertSesToCircuitJson(MOTOR_DRIVER_SES)
23+
// Parse SES for traces and vias
24+
const sesCircuitJson = convertSesToCircuitJson(MOTOR_DRIVER_SES)
1625

17-
const traces = circuitJson.filter(
26+
const traces = sesCircuitJson.filter(
1827
(el): el is PcbTrace => el.type === "pcb_trace",
1928
)
20-
const vias = circuitJson.filter(
29+
const vias = sesCircuitJson.filter(
2130
(el): el is PcbVia => el.type === "pcb_via",
2231
)
2332

33+
// Parse DSN for SMT pads and plated holes
34+
const dsnCircuitJson = convertDsnToCircuitJson(MOTOR_DRIVER_DSN)
35+
36+
const smtpads = dsnCircuitJson.filter(
37+
(el): el is PcbSmtPad => el.type === "pcb_smtpad",
38+
)
39+
const platedHoles = dsnCircuitJson.filter(
40+
(el): el is PcbPlatedHole => el.type === "pcb_plated_hole",
41+
)
42+
2443
return new TraceViewer({
2544
traces,
2645
vias,
46+
smtpads,
47+
platedHoles,
2748
colorMode,
2849
})
2950
} catch (e) {
30-
console.error("Failed to parse SES:", e)
51+
console.error("Failed to parse SES/DSN:", e)
3152
return new TraceViewer({ traces: [], vias: [], colorMode })
3253
}
3354
}, [colorMode])

site/repro02/LGA51x4_net15_bottom_only.page.tsx renamed to site/repro02/LGA15x4_net15_bottom_only.page.tsx

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,54 @@
11
import React, { useMemo, useState, useCallback } from "react"
22
import { GenericSolverDebugger } from "@tscircuit/solver-utils/react"
3-
import { TraceViewer, type ColorMode } from "../TraceViewer"
3+
import { TraceViewer, type ColorMode } from "../SesToCircuitJsonPipelineSolver"
44
import { convertSesToCircuitJson } from "../../lib/ses-to-pcb"
5-
import type { CircuitJson, PcbTrace, PcbVia } from "circuit-json"
5+
import { convertDsnToCircuitJson } from "../../lib/dsn-to-pcb"
6+
import type {
7+
PcbTrace,
8+
PcbVia,
9+
PcbSmtPad,
10+
PcbPlatedHole,
11+
} from "circuit-json"
612
// @ts-ignore
7-
import LGA51x4_net15_bottom_only_SES from "./assets/LGA51x4_net15_bottom_only.ses?raw"
13+
import LGA51x4_net15_bottom_only_SES from "./assets/LGA15x4_net15_bottom_only.ses?raw"
14+
// @ts-ignore
15+
import LGA51x4_net15_bottom_only_DSN from "./assets/LGA15x4_net15_bottom_only_input.dsn?raw"
816

917
export default function LGA51x4_net15_bottom_onlyFixture() {
1018
const [colorMode, setColorMode] = useState<ColorMode>("layer")
1119
const [solverKey, setSolverKey] = useState(0)
1220

1321
const solver = useMemo(() => {
1422
try {
15-
const circuitJson = convertSesToCircuitJson(LGA51x4_net15_bottom_only_SES)
23+
// Parse SES for traces and vias
24+
const sesCircuitJson = convertSesToCircuitJson(LGA51x4_net15_bottom_only_SES)
1625

17-
const traces = circuitJson.filter(
26+
const traces = sesCircuitJson.filter(
1827
(el): el is PcbTrace => el.type === "pcb_trace",
1928
)
20-
const vias = circuitJson.filter(
29+
const vias = sesCircuitJson.filter(
2130
(el): el is PcbVia => el.type === "pcb_via",
2231
)
2332

33+
// Parse DSN for SMT pads and plated holes
34+
const dsnCircuitJson = convertDsnToCircuitJson(LGA51x4_net15_bottom_only_DSN)
35+
36+
const smtpads = dsnCircuitJson.filter(
37+
(el): el is PcbSmtPad => el.type === "pcb_smtpad",
38+
)
39+
const platedHoles = dsnCircuitJson.filter(
40+
(el): el is PcbPlatedHole => el.type === "pcb_plated_hole",
41+
)
42+
2443
return new TraceViewer({
2544
traces,
2645
vias,
46+
smtpads,
47+
platedHoles,
2748
colorMode,
2849
})
2950
} catch (e) {
30-
console.error("Failed to parse SES:", e)
51+
console.error("Failed to parse SES/DSN:", e)
3152
return new TraceViewer({ traces: [], vias: [], colorMode })
3253
}
3354
}, [colorMode])
File renamed without changes.

site/repro02/assets/LGA51x4_net15_bottom_only_input.dsn renamed to site/repro02/assets/LGA15x4_net15_bottom_only_input.dsn

File renamed without changes.

0 commit comments

Comments
 (0)