circuit-json-to-lbrn 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,37 +1,26 @@
1
+ import { Box } from "@flatten-js/core"
1
2
  import type { ConvertContext } from "../../ConvertContext"
2
3
  import type { PcbSmtPadRect } from "circuit-json"
3
4
  import { ShapePath } from "lbrnts"
4
5
 
5
6
  export const addRectSmtPad = (smtPad: PcbSmtPadRect, ctx: ConvertContext) => {
6
- const { project, copperCutSetting } = ctx
7
+ const { project, copperCutSetting, connMap, netGeoms, origin } = ctx
7
8
 
8
- const centerX = smtPad.x
9
- const centerY = smtPad.y
9
+ const centerX = smtPad.x + origin.x
10
+ const centerY = smtPad.y + origin.y
10
11
  const halfWidth = smtPad.width / 2
11
12
  const halfHeight = smtPad.height / 2
12
13
 
13
- // Create vertices for rectangle corners (clockwise from top-left)
14
- const verts = [
15
- { x: centerX - halfWidth, y: centerY - halfHeight }, // Top-left
16
- { x: centerX + halfWidth, y: centerY - halfHeight }, // Top-right
17
- { x: centerX + halfWidth, y: centerY + halfHeight }, // Bottom-right
18
- { x: centerX - halfWidth, y: centerY + halfHeight }, // Bottom-left
19
- ]
14
+ const netId = connMap.getNetConnectedToId(smtPad.pcb_smtpad_id)!
20
15
 
21
- // Create primitives (all lines)
22
- const prims = [
23
- { type: 0 }, // LineTo for top-right
24
- { type: 0 }, // LineTo for bottom-right
25
- { type: 0 }, // LineTo for bottom-left
26
- { type: 0 }, // LineTo back to top-left (closing)
27
- ]
28
-
29
- project.children.push(
30
- new ShapePath({
31
- cutIndex: copperCutSetting.index,
32
- verts,
33
- prims,
34
- isClosed: true,
35
- }),
36
- )
16
+ ctx.netGeoms
17
+ .get(netId)
18
+ ?.push(
19
+ new Box(
20
+ centerX - halfWidth,
21
+ centerY - halfHeight,
22
+ centerX + halfWidth,
23
+ centerY + halfHeight,
24
+ ),
25
+ )
37
26
  }
package/lib/index.ts CHANGED
@@ -1,13 +1,21 @@
1
1
  import type { CircuitJson } from "circuit-json"
2
- import { LightBurnProject, CutSetting } from "lbrnts"
2
+ import { LightBurnProject, CutSetting, ShapePath } from "lbrnts"
3
3
  import { cju } from "@tscircuit/circuit-json-util"
4
4
  import type { ConvertContext } from "./ConvertContext"
5
5
  import { addPlatedHole } from "./element-handlers/addPlatedHole"
6
6
  import { addSmtPad } from "./element-handlers/addSmtPad"
7
+ import { addPcbTrace } from "./element-handlers/addPcbTrace"
8
+ import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map"
9
+ import { Polygon, Box, BooleanOperations } from "@flatten-js/core"
10
+ import { polygonToShapePathData } from "./polygon-to-shape-path"
11
+ // import { writeDebugSvg } from "./writeDebugSvg"
7
12
 
8
13
  export const convertCircuitJsonToLbrn = (
9
14
  circuitJson: CircuitJson,
10
- options: { includeSilkscreen?: boolean } = {},
15
+ options: {
16
+ includeSilkscreen?: boolean
17
+ origin?: { x: number; y: number }
18
+ } = {},
11
19
  ): LightBurnProject => {
12
20
  const db = cju(circuitJson)
13
21
  const project = new LightBurnProject({
@@ -23,19 +31,98 @@ export const convertCircuitJsonToLbrn = (
23
31
  })
24
32
  project.children.push(copperCutSetting)
25
33
 
34
+ const connMap = getFullConnectivityMapFromCircuitJson(circuitJson)
35
+
26
36
  const ctx: ConvertContext = {
27
37
  db,
28
38
  project,
29
39
  copperCutSetting,
40
+ connMap,
41
+ netGeoms: new Map(),
42
+ origin: options.origin ?? { x: 0, y: 0 },
43
+ }
44
+
45
+ for (const net of Object.keys(connMap.netMap)) {
46
+ ctx.netGeoms.set(net, [])
30
47
  }
31
48
 
32
49
  for (const smtpad of db.pcb_smtpad.list()) {
33
50
  addSmtPad(smtpad, ctx)
34
51
  }
35
52
 
36
- for (const platedHole of db.pcb_plated_hole.list()) {
37
- addPlatedHole(platedHole, ctx)
53
+ // for (const platedHole of db.pcb_plated_hole.list()) {
54
+ // addPlatedHole(platedHole, ctx)
55
+ // }
56
+
57
+ for (const trace of db.pcb_trace.list()) {
58
+ addPcbTrace(trace, ctx)
38
59
  }
39
60
 
61
+ // Draw each individual shape geometry as a ShapePath
62
+ // FOR DEBUGGING!!!
63
+ // for (const net of Object.keys(connMap.netMap)) {
64
+ // const netGeoms = ctx.netGeoms.get(net)!
65
+
66
+ // if (netGeoms.length === 0) {
67
+ // continue
68
+ // }
69
+
70
+ // for (const geom of netGeoms) {
71
+ // // Convert Box to Polygon if needed
72
+ // const polygon = geom instanceof Box ? new Polygon(geom) : geom
73
+
74
+ // // Convert the polygon to verts and prims
75
+ // const { verts, prims } = polygonToShapePathData(polygon)
76
+
77
+ // project.children.push(
78
+ // new ShapePath({
79
+ // cutIndex: copperCutSetting.index,
80
+ // verts,
81
+ // prims,
82
+ // isClosed: false,
83
+ // }),
84
+ // )
85
+ // }
86
+ // }
87
+
88
+ // Create a union of all the net geoms, and add to project
89
+ for (const net of Object.keys(connMap.netMap)) {
90
+ const netGeoms = ctx.netGeoms.get(net)!
91
+
92
+ if (netGeoms.length === 0) {
93
+ continue
94
+ }
95
+
96
+ let union = netGeoms[0]!
97
+ if (union instanceof Box) {
98
+ union = new Polygon(union)
99
+ }
100
+ for (const geom of netGeoms.slice(1)) {
101
+ if (geom instanceof Polygon) {
102
+ union = BooleanOperations.unify(union, geom)
103
+ } else if (geom instanceof Box) {
104
+ union = BooleanOperations.unify(union, new Polygon(geom))
105
+ }
106
+ }
107
+
108
+ // DEBUGGING ONLY!!!
109
+ // if (netGeoms.length > 1) {
110
+ // writeDebugSvg(net, union)
111
+ // }
112
+
113
+ for (const island of union.splitToIslands()) {
114
+ // Convert the polygon to verts and prims
115
+ const { verts, prims } = polygonToShapePathData(island)
116
+
117
+ project.children.push(
118
+ new ShapePath({
119
+ cutIndex: copperCutSetting.index,
120
+ verts,
121
+ prims,
122
+ isClosed: false,
123
+ }),
124
+ )
125
+ }
126
+ }
40
127
  return project
41
128
  }
@@ -0,0 +1,49 @@
1
+ import type { Polygon } from "@flatten-js/core"
2
+
3
+ export interface ShapePathData {
4
+ verts: Array<{ x: number; y: number }>
5
+ prims: Array<{ type: number }>
6
+ }
7
+
8
+ /**
9
+ * Converts a flattenjs Polygon to verts and prims arrays for use with ShapePath
10
+ * @param polygon The flattenjs Polygon to convert
11
+ * @returns Object containing verts and prims arrays
12
+ */
13
+ export function polygonToShapePathData(polygon: Polygon): ShapePathData {
14
+ const verts: Array<{ x: number; y: number }> = []
15
+ const prims: Array<{ type: number }> = []
16
+
17
+ // Get SVG representation and extract path data
18
+ const svgString = polygon.svg()
19
+ const dAttributeMatch = svgString.match(/\bd="([^"]+)"/)
20
+
21
+ if (!dAttributeMatch || !dAttributeMatch[1]) {
22
+ return { verts, prims }
23
+ }
24
+
25
+ const pathData = dAttributeMatch[1].trim()
26
+
27
+ // Parse SVG path commands (M, L, z)
28
+ const commands = pathData.match(/[MLz][^MLz]*/g) || []
29
+
30
+ for (const command of commands) {
31
+ const type = command[0]
32
+ const coords = command.slice(1).trim()
33
+
34
+ if (type === "M" || type === "L") {
35
+ // Parse coordinates (format: "x,y")
36
+ const parts = coords.split(",")
37
+ const x = Number(parts[0])
38
+ const y = Number(parts[1])
39
+
40
+ if (!Number.isNaN(x) && !Number.isNaN(y)) {
41
+ verts.push({ x, y })
42
+ prims.push({ type: 0 }) // 0 = LineTo
43
+ }
44
+ }
45
+ // 'z' closes the path, no action needed
46
+ }
47
+
48
+ return { verts, prims }
49
+ }
@@ -0,0 +1,43 @@
1
+ import { Polygon } from "@flatten-js/core"
2
+
3
+ export const writeDebugSvg = (netName: string, polygon: Polygon) => {
4
+ // Calculate bounds from all points in the polygon
5
+ let minX = Infinity
6
+ let minY = Infinity
7
+ let maxX = -Infinity
8
+ let maxY = -Infinity
9
+
10
+ for (const face of polygon.faces) {
11
+ for (const edge of face.edges) {
12
+ const start = edge.start
13
+ const end = edge.end
14
+
15
+ minX = Math.min(minX, start.x, end.x)
16
+ minY = Math.min(minY, start.y, end.y)
17
+ maxX = Math.max(maxX, start.x, end.x)
18
+ maxY = Math.max(maxY, start.y, end.y)
19
+ }
20
+ }
21
+
22
+ // Calculate bounds with 15% margin
23
+ const width = maxX - minX
24
+ const height = maxY - minY
25
+ const margin = 0.15
26
+
27
+ const viewBoxX = minX - width * margin
28
+ const viewBoxY = minY - height * margin
29
+ const viewBoxWidth = width * (1 + 2 * margin)
30
+ const viewBoxHeight = height * (1 + 2 * margin)
31
+
32
+ // Calculate stroke width as 0.5% of max viewBox dimension
33
+ const maxDimension = Math.max(viewBoxWidth, viewBoxHeight)
34
+ const strokeWidth = maxDimension * 0.001
35
+
36
+ const svgContent = `<svg width="800" height="800" style="background-color: #ffffff;" viewBox="${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}" xmlns="http://www.w3.org/2000/svg">
37
+ <g stroke-width="${strokeWidth}">
38
+ ${polygon.svg()}
39
+ </g>
40
+ </svg>`
41
+
42
+ Bun.write(`debug-output/${netName}.svg`, svgContent)
43
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "circuit-json-to-lbrn",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "tsup-node ./lib/index.ts --dts --format esm",
@@ -10,6 +10,8 @@
10
10
  },
11
11
  "devDependencies": {
12
12
  "@biomejs/biome": "^2.3.6",
13
+ "@flatten-js/core": "^1.6.8",
14
+ "@flatten-js/polygon-offset": "^1.1.4",
13
15
  "@tscircuit/circuit-json-util": "^0.0.72",
14
16
  "@types/bun": "latest",
15
17
  "bun-match-svg": "^0.0.14",
@@ -24,6 +26,6 @@
24
26
  "typescript": "^5"
25
27
  },
26
28
  "dependencies": {
27
- "lbrnts": "^0.0.1"
29
+ "lbrnts": "^0.0.6"
28
30
  }
29
31
  }
File without changes
package/site/main.tsx ADDED
File without changes