circuit-json-to-lbrn 0.0.1
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.
- package/.claude/settings.local.json +9 -0
- package/README.md +13 -0
- package/biome.json +93 -0
- package/bunfig.toml +5 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +215 -0
- package/lib/ConvertContext.ts +9 -0
- package/lib/element-handlers/addPlatedHole/addCirclePlatedHole.ts +83 -0
- package/lib/element-handlers/addPlatedHole/addCircularHoleWithRectPad.ts +12 -0
- package/lib/element-handlers/addPlatedHole/addHoleWithPolygonPad.ts +12 -0
- package/lib/element-handlers/addPlatedHole/addOvalPlatedHole.ts +12 -0
- package/lib/element-handlers/addPlatedHole/addPillHoleWithRectPad.ts +12 -0
- package/lib/element-handlers/addPlatedHole/addRotatedPillHoleWithRectPad.ts +12 -0
- package/lib/element-handlers/addPlatedHole/index.ts +43 -0
- package/lib/element-handlers/addSmtPad/addRectSmtPad.ts +37 -0
- package/lib/element-handlers/addSmtPad/index.ts +15 -0
- package/lib/index.ts +41 -0
- package/package.json +29 -0
- package/tests/__snapshots__/svg.snap.svg +3 -0
- package/tests/assets/lga-interconnect.circuit.json +6170 -0
- package/tests/examples/__snapshots__/lga-interconnect.snap.svg +8 -0
- package/tests/examples/lga-interconnect.test.ts +20 -0
- package/tests/fixtures/preload.ts +1 -0
- package/tests/plated-hole.test.ts +70 -0
- package/tests/svg.test.ts +12 -0
- package/tsconfig.json +32 -0
- package/types.d.ts +4 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# circuit-json-to-lbrn
|
|
2
|
+
|
|
3
|
+
Convert Circuit JSON to LBRN XML for PCB fabrication via laser ablation.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { convertCircuitJsonToLbrn } from "circuit-json-to-lbrn"
|
|
9
|
+
|
|
10
|
+
const lbrnXml = convertCircuitJsonToLbrn(circuitJson, {
|
|
11
|
+
includeSilkscreen: true,
|
|
12
|
+
})
|
|
13
|
+
```
|
package/biome.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
|
|
3
|
+
"assist": { "actions": { "source": { "organizeImports": "on" } } },
|
|
4
|
+
"formatter": {
|
|
5
|
+
"enabled": true,
|
|
6
|
+
"indentStyle": "space"
|
|
7
|
+
},
|
|
8
|
+
"files": {
|
|
9
|
+
"includes": ["**", "!**/cosmos-export", "!**/dist", "!**/package.json"]
|
|
10
|
+
},
|
|
11
|
+
"javascript": {
|
|
12
|
+
"formatter": {
|
|
13
|
+
"jsxQuoteStyle": "double",
|
|
14
|
+
"quoteProperties": "asNeeded",
|
|
15
|
+
"trailingCommas": "all",
|
|
16
|
+
"semicolons": "asNeeded",
|
|
17
|
+
"arrowParentheses": "always",
|
|
18
|
+
"bracketSpacing": true,
|
|
19
|
+
"bracketSameLine": false
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"linter": {
|
|
23
|
+
"enabled": true,
|
|
24
|
+
"rules": {
|
|
25
|
+
"recommended": true,
|
|
26
|
+
"suspicious": {
|
|
27
|
+
"noExplicitAny": "off"
|
|
28
|
+
},
|
|
29
|
+
"complexity": {
|
|
30
|
+
"noForEach": "error",
|
|
31
|
+
"useLiteralKeys": "off"
|
|
32
|
+
},
|
|
33
|
+
"a11y": {
|
|
34
|
+
"noAccessKey": "off",
|
|
35
|
+
"noAriaHiddenOnFocusable": "off",
|
|
36
|
+
"noAriaUnsupportedElements": "off",
|
|
37
|
+
"noAutofocus": "off",
|
|
38
|
+
"noDistractingElements": "off",
|
|
39
|
+
"noHeaderScope": "off",
|
|
40
|
+
"noInteractiveElementToNoninteractiveRole": "off",
|
|
41
|
+
"noLabelWithoutControl": "off",
|
|
42
|
+
"noNoninteractiveElementToInteractiveRole": "off",
|
|
43
|
+
"noNoninteractiveTabindex": "off",
|
|
44
|
+
"noPositiveTabindex": "off",
|
|
45
|
+
"noRedundantAlt": "off",
|
|
46
|
+
"noRedundantRoles": "off",
|
|
47
|
+
"noStaticElementInteractions": "off",
|
|
48
|
+
"noSvgWithoutTitle": "off",
|
|
49
|
+
"useAltText": "off",
|
|
50
|
+
"useAnchorContent": "off",
|
|
51
|
+
"useAriaActivedescendantWithTabindex": "off",
|
|
52
|
+
"useAriaPropsForRole": "off",
|
|
53
|
+
"useAriaPropsSupportedByRole": "off",
|
|
54
|
+
"useButtonType": "off",
|
|
55
|
+
"useFocusableInteractive": "off",
|
|
56
|
+
"useHeadingContent": "off",
|
|
57
|
+
"useHtmlLang": "off",
|
|
58
|
+
"useIframeTitle": "off",
|
|
59
|
+
"useKeyWithClickEvents": "off",
|
|
60
|
+
"useKeyWithMouseEvents": "off",
|
|
61
|
+
"useMediaCaption": "off",
|
|
62
|
+
"useSemanticElements": "off",
|
|
63
|
+
"useValidAnchor": "off",
|
|
64
|
+
"useValidAriaProps": "off",
|
|
65
|
+
"useValidAriaRole": "off",
|
|
66
|
+
"useValidAriaValues": "off",
|
|
67
|
+
"useValidAutocomplete": "off",
|
|
68
|
+
"useValidLang": "off"
|
|
69
|
+
},
|
|
70
|
+
"style": {
|
|
71
|
+
"useSingleVarDeclarator": "error",
|
|
72
|
+
"noParameterAssign": "off",
|
|
73
|
+
"noUselessElse": "off",
|
|
74
|
+
"noNonNullAssertion": "off",
|
|
75
|
+
"useNumberNamespace": "off",
|
|
76
|
+
"noUnusedTemplateLiteral": "off",
|
|
77
|
+
"useFilenamingConvention": {
|
|
78
|
+
"level": "error",
|
|
79
|
+
"options": {
|
|
80
|
+
"strictCase": true,
|
|
81
|
+
"requireAscii": true,
|
|
82
|
+
"filenameCases": ["kebab-case", "export"]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"useAsConstAssertion": "error",
|
|
86
|
+
"useDefaultParameterLast": "error",
|
|
87
|
+
"useEnumInitializers": "error",
|
|
88
|
+
"useSelfClosingElements": "error",
|
|
89
|
+
"noInferrableTypes": "error"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
package/bunfig.toml
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { CircuitJson } from 'circuit-json';
|
|
2
|
+
import { LightBurnProject } from 'lbrnts';
|
|
3
|
+
|
|
4
|
+
declare const convertCircuitJsonToLbrn: (circuitJson: CircuitJson, options?: {
|
|
5
|
+
includeSilkscreen?: boolean;
|
|
6
|
+
}) => LightBurnProject;
|
|
7
|
+
|
|
8
|
+
export { convertCircuitJsonToLbrn };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// lib/index.ts
|
|
2
|
+
import { LightBurnProject, CutSetting } from "lbrnts";
|
|
3
|
+
import { cju } from "@tscircuit/circuit-json-util";
|
|
4
|
+
|
|
5
|
+
// lib/element-handlers/addPlatedHole/addCirclePlatedHole.ts
|
|
6
|
+
import { ShapePath } from "lbrnts";
|
|
7
|
+
var addCirclePlatedHole = (platedHole, ctx) => {
|
|
8
|
+
const { project, copperCutSetting } = ctx;
|
|
9
|
+
const radius = platedHole.outer_diameter / 2;
|
|
10
|
+
const centerX = platedHole.x;
|
|
11
|
+
const centerY = platedHole.y;
|
|
12
|
+
const kappa = 0.5522847498;
|
|
13
|
+
const controlOffset = radius * kappa;
|
|
14
|
+
const verts = [
|
|
15
|
+
// Right point (3 o'clock)
|
|
16
|
+
{ x: centerX + radius, y: centerY },
|
|
17
|
+
// Bottom point (6 o'clock) with control points
|
|
18
|
+
{
|
|
19
|
+
x: centerX,
|
|
20
|
+
y: centerY + radius,
|
|
21
|
+
c: 1,
|
|
22
|
+
c0x: radius,
|
|
23
|
+
c0y: controlOffset,
|
|
24
|
+
c1x: controlOffset,
|
|
25
|
+
c1y: radius
|
|
26
|
+
},
|
|
27
|
+
// Left point (9 o'clock) with control points
|
|
28
|
+
{
|
|
29
|
+
x: centerX - radius,
|
|
30
|
+
y: centerY,
|
|
31
|
+
c: 1,
|
|
32
|
+
c0x: -controlOffset,
|
|
33
|
+
c0y: radius,
|
|
34
|
+
c1x: -radius,
|
|
35
|
+
c1y: controlOffset
|
|
36
|
+
},
|
|
37
|
+
// Top point (12 o'clock) with control points
|
|
38
|
+
{
|
|
39
|
+
x: centerX,
|
|
40
|
+
y: centerY - radius,
|
|
41
|
+
c: 1,
|
|
42
|
+
c0x: -radius,
|
|
43
|
+
c0y: -controlOffset,
|
|
44
|
+
c1x: -controlOffset,
|
|
45
|
+
c1y: -radius
|
|
46
|
+
},
|
|
47
|
+
// Back to right point (close path) with control points
|
|
48
|
+
{
|
|
49
|
+
x: centerX + radius,
|
|
50
|
+
y: centerY,
|
|
51
|
+
c: 1,
|
|
52
|
+
c0x: controlOffset,
|
|
53
|
+
c0y: -radius,
|
|
54
|
+
c1x: radius,
|
|
55
|
+
c1y: -controlOffset
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
const prims = [
|
|
59
|
+
{ type: 1 },
|
|
60
|
+
// BezierTo for bottom
|
|
61
|
+
{ type: 1 },
|
|
62
|
+
// BezierTo for left
|
|
63
|
+
{ type: 1 },
|
|
64
|
+
// BezierTo for top
|
|
65
|
+
{ type: 1 }
|
|
66
|
+
// BezierTo for right (closing)
|
|
67
|
+
];
|
|
68
|
+
project.children.push(
|
|
69
|
+
new ShapePath({
|
|
70
|
+
cutIndex: copperCutSetting.index,
|
|
71
|
+
verts,
|
|
72
|
+
prims,
|
|
73
|
+
isClosed: true
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// lib/element-handlers/addPlatedHole/addOvalPlatedHole.ts
|
|
79
|
+
var addOvalPlatedHole = (platedHole, ctx) => {
|
|
80
|
+
console.warn(
|
|
81
|
+
`Oval/pill plated hole not yet implemented: ${platedHole.pcb_plated_hole_id}`
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// lib/element-handlers/addPlatedHole/addCircularHoleWithRectPad.ts
|
|
86
|
+
var addCircularHoleWithRectPad = (platedHole, ctx) => {
|
|
87
|
+
console.warn(
|
|
88
|
+
`Circular hole with rect pad not yet implemented: ${platedHole.pcb_plated_hole_id}`
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// lib/element-handlers/addPlatedHole/addPillHoleWithRectPad.ts
|
|
93
|
+
var addPillHoleWithRectPad = (platedHole, ctx) => {
|
|
94
|
+
console.warn(
|
|
95
|
+
`Pill hole with rect pad not yet implemented: ${platedHole.pcb_plated_hole_id}`
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// lib/element-handlers/addPlatedHole/addRotatedPillHoleWithRectPad.ts
|
|
100
|
+
var addRotatedPillHoleWithRectPad = (platedHole, ctx) => {
|
|
101
|
+
console.warn(
|
|
102
|
+
`Rotated pill hole with rect pad not yet implemented: ${platedHole.pcb_plated_hole_id}`
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// lib/element-handlers/addPlatedHole/addHoleWithPolygonPad.ts
|
|
107
|
+
var addHoleWithPolygonPad = (platedHole, ctx) => {
|
|
108
|
+
console.warn(
|
|
109
|
+
`Hole with polygon pad not yet implemented: ${platedHole.pcb_plated_hole_id}`
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// lib/element-handlers/addPlatedHole/index.ts
|
|
114
|
+
var addPlatedHole = (platedHole, ctx) => {
|
|
115
|
+
switch (platedHole.shape) {
|
|
116
|
+
case "circle":
|
|
117
|
+
return addCirclePlatedHole(platedHole, ctx);
|
|
118
|
+
case "oval":
|
|
119
|
+
case "pill":
|
|
120
|
+
return addOvalPlatedHole(platedHole, ctx);
|
|
121
|
+
case "circular_hole_with_rect_pad":
|
|
122
|
+
return addCircularHoleWithRectPad(platedHole, ctx);
|
|
123
|
+
case "pill_hole_with_rect_pad":
|
|
124
|
+
return addPillHoleWithRectPad(platedHole, ctx);
|
|
125
|
+
case "rotated_pill_hole_with_rect_pad":
|
|
126
|
+
return addRotatedPillHoleWithRectPad(platedHole, ctx);
|
|
127
|
+
case "hole_with_polygon_pad":
|
|
128
|
+
return addHoleWithPolygonPad(platedHole, ctx);
|
|
129
|
+
default:
|
|
130
|
+
const _exhaustive = platedHole;
|
|
131
|
+
console.warn(`Unknown plated hole shape: ${platedHole.shape}`);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// lib/element-handlers/addSmtPad/addRectSmtPad.ts
|
|
136
|
+
import { ShapePath as ShapePath2 } from "lbrnts";
|
|
137
|
+
var addRectSmtPad = (smtPad, ctx) => {
|
|
138
|
+
const { project, copperCutSetting } = ctx;
|
|
139
|
+
const centerX = smtPad.x;
|
|
140
|
+
const centerY = smtPad.y;
|
|
141
|
+
const halfWidth = smtPad.width / 2;
|
|
142
|
+
const halfHeight = smtPad.height / 2;
|
|
143
|
+
const verts = [
|
|
144
|
+
{ x: centerX - halfWidth, y: centerY - halfHeight },
|
|
145
|
+
// Top-left
|
|
146
|
+
{ x: centerX + halfWidth, y: centerY - halfHeight },
|
|
147
|
+
// Top-right
|
|
148
|
+
{ x: centerX + halfWidth, y: centerY + halfHeight },
|
|
149
|
+
// Bottom-right
|
|
150
|
+
{ x: centerX - halfWidth, y: centerY + halfHeight }
|
|
151
|
+
// Bottom-left
|
|
152
|
+
];
|
|
153
|
+
const prims = [
|
|
154
|
+
{ type: 0 },
|
|
155
|
+
// LineTo for top-right
|
|
156
|
+
{ type: 0 },
|
|
157
|
+
// LineTo for bottom-right
|
|
158
|
+
{ type: 0 },
|
|
159
|
+
// LineTo for bottom-left
|
|
160
|
+
{ type: 0 }
|
|
161
|
+
// LineTo back to top-left (closing)
|
|
162
|
+
];
|
|
163
|
+
project.children.push(
|
|
164
|
+
new ShapePath2({
|
|
165
|
+
cutIndex: copperCutSetting.index,
|
|
166
|
+
verts,
|
|
167
|
+
prims,
|
|
168
|
+
isClosed: true
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// lib/element-handlers/addSmtPad/index.ts
|
|
174
|
+
var addSmtPad = (smtPad, ctx) => {
|
|
175
|
+
switch (smtPad.shape) {
|
|
176
|
+
case "rect": {
|
|
177
|
+
addRectSmtPad(smtPad, ctx);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
default: {
|
|
181
|
+
throw new Error(`Unknown smt pad shape: ${smtPad.shape}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// lib/index.ts
|
|
187
|
+
var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
188
|
+
const db = cju(circuitJson);
|
|
189
|
+
const project = new LightBurnProject({
|
|
190
|
+
appVersion: "1.7.03",
|
|
191
|
+
formatVersion: "1"
|
|
192
|
+
});
|
|
193
|
+
const copperCutSetting = new CutSetting({
|
|
194
|
+
index: 0,
|
|
195
|
+
name: "Cut Copper",
|
|
196
|
+
numPasses: 12,
|
|
197
|
+
speed: 100
|
|
198
|
+
});
|
|
199
|
+
project.children.push(copperCutSetting);
|
|
200
|
+
const ctx = {
|
|
201
|
+
db,
|
|
202
|
+
project,
|
|
203
|
+
copperCutSetting
|
|
204
|
+
};
|
|
205
|
+
for (const smtpad of db.pcb_smtpad.list()) {
|
|
206
|
+
addSmtPad(smtpad, ctx);
|
|
207
|
+
}
|
|
208
|
+
for (const platedHole of db.pcb_plated_hole.list()) {
|
|
209
|
+
addPlatedHole(platedHole, ctx);
|
|
210
|
+
}
|
|
211
|
+
return project;
|
|
212
|
+
};
|
|
213
|
+
export {
|
|
214
|
+
convertCircuitJsonToLbrn
|
|
215
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CircuitJsonUtilObjects } from "@tscircuit/circuit-json-util"
|
|
2
|
+
import type { CutSetting, LightBurnProject } from "lbrnts"
|
|
3
|
+
|
|
4
|
+
export interface ConvertContext {
|
|
5
|
+
db: CircuitJsonUtilObjects
|
|
6
|
+
project: LightBurnProject
|
|
7
|
+
|
|
8
|
+
copperCutSetting: CutSetting
|
|
9
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { PcbPlatedHoleCircle } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
import { ShapePath } from "lbrnts"
|
|
4
|
+
|
|
5
|
+
export const addCirclePlatedHole = (
|
|
6
|
+
platedHole: PcbPlatedHoleCircle,
|
|
7
|
+
ctx: ConvertContext,
|
|
8
|
+
): void => {
|
|
9
|
+
const { project, copperCutSetting } = ctx
|
|
10
|
+
|
|
11
|
+
// Create a circle path using the outer diameter
|
|
12
|
+
const radius = platedHole.outer_diameter / 2
|
|
13
|
+
const centerX = platedHole.x
|
|
14
|
+
const centerY = platedHole.y
|
|
15
|
+
|
|
16
|
+
// Create circle using 4 bezier curves (standard circle approximation)
|
|
17
|
+
// The control point offset for a circle is approximately 0.5522847498 * radius
|
|
18
|
+
const kappa = 0.5522847498
|
|
19
|
+
const controlOffset = radius * kappa
|
|
20
|
+
|
|
21
|
+
// Create vertices for a circle starting from the right point (3 o'clock)
|
|
22
|
+
const verts = [
|
|
23
|
+
// Right point (3 o'clock)
|
|
24
|
+
{ x: centerX + radius, y: centerY },
|
|
25
|
+
// Bottom point (6 o'clock) with control points
|
|
26
|
+
{
|
|
27
|
+
x: centerX,
|
|
28
|
+
y: centerY + radius,
|
|
29
|
+
c: 1,
|
|
30
|
+
c0x: radius,
|
|
31
|
+
c0y: controlOffset,
|
|
32
|
+
c1x: controlOffset,
|
|
33
|
+
c1y: radius,
|
|
34
|
+
},
|
|
35
|
+
// Left point (9 o'clock) with control points
|
|
36
|
+
{
|
|
37
|
+
x: centerX - radius,
|
|
38
|
+
y: centerY,
|
|
39
|
+
c: 1,
|
|
40
|
+
c0x: -controlOffset,
|
|
41
|
+
c0y: radius,
|
|
42
|
+
c1x: -radius,
|
|
43
|
+
c1y: controlOffset,
|
|
44
|
+
},
|
|
45
|
+
// Top point (12 o'clock) with control points
|
|
46
|
+
{
|
|
47
|
+
x: centerX,
|
|
48
|
+
y: centerY - radius,
|
|
49
|
+
c: 1,
|
|
50
|
+
c0x: -radius,
|
|
51
|
+
c0y: -controlOffset,
|
|
52
|
+
c1x: -controlOffset,
|
|
53
|
+
c1y: -radius,
|
|
54
|
+
},
|
|
55
|
+
// Back to right point (close path) with control points
|
|
56
|
+
{
|
|
57
|
+
x: centerX + radius,
|
|
58
|
+
y: centerY,
|
|
59
|
+
c: 1,
|
|
60
|
+
c0x: controlOffset,
|
|
61
|
+
c0y: -radius,
|
|
62
|
+
c1x: radius,
|
|
63
|
+
c1y: -controlOffset,
|
|
64
|
+
},
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
// Create primitives (all bezier curves)
|
|
68
|
+
const prims = [
|
|
69
|
+
{ type: 1 }, // BezierTo for bottom
|
|
70
|
+
{ type: 1 }, // BezierTo for left
|
|
71
|
+
{ type: 1 }, // BezierTo for top
|
|
72
|
+
{ type: 1 }, // BezierTo for right (closing)
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
project.children.push(
|
|
76
|
+
new ShapePath({
|
|
77
|
+
cutIndex: copperCutSetting.index,
|
|
78
|
+
verts,
|
|
79
|
+
prims,
|
|
80
|
+
isClosed: true,
|
|
81
|
+
}),
|
|
82
|
+
)
|
|
83
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PcbHoleCircularWithRectPad } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
|
|
4
|
+
export const addCircularHoleWithRectPad = (
|
|
5
|
+
platedHole: PcbHoleCircularWithRectPad,
|
|
6
|
+
ctx: ConvertContext,
|
|
7
|
+
): void => {
|
|
8
|
+
// TODO: Implement circular hole with rectangular pad generation
|
|
9
|
+
console.warn(
|
|
10
|
+
`Circular hole with rect pad not yet implemented: ${platedHole.pcb_plated_hole_id}`,
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PcbHoleWithPolygonPad } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
|
|
4
|
+
export const addHoleWithPolygonPad = (
|
|
5
|
+
platedHole: PcbHoleWithPolygonPad,
|
|
6
|
+
ctx: ConvertContext,
|
|
7
|
+
): void => {
|
|
8
|
+
// TODO: Implement hole with polygon pad generation
|
|
9
|
+
console.warn(
|
|
10
|
+
`Hole with polygon pad not yet implemented: ${platedHole.pcb_plated_hole_id}`,
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PcbPlatedHoleOval } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
|
|
4
|
+
export const addOvalPlatedHole = (
|
|
5
|
+
platedHole: PcbPlatedHoleOval,
|
|
6
|
+
ctx: ConvertContext,
|
|
7
|
+
): void => {
|
|
8
|
+
// TODO: Implement oval/pill plated hole generation
|
|
9
|
+
console.warn(
|
|
10
|
+
`Oval/pill plated hole not yet implemented: ${platedHole.pcb_plated_hole_id}`,
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PcbHolePillWithRectPad } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
|
|
4
|
+
export const addPillHoleWithRectPad = (
|
|
5
|
+
platedHole: PcbHolePillWithRectPad,
|
|
6
|
+
ctx: ConvertContext,
|
|
7
|
+
): void => {
|
|
8
|
+
// TODO: Implement pill hole with rectangular pad generation
|
|
9
|
+
console.warn(
|
|
10
|
+
`Pill hole with rect pad not yet implemented: ${platedHole.pcb_plated_hole_id}`,
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PcbHoleRotatedPillWithRectPad } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
|
|
4
|
+
export const addRotatedPillHoleWithRectPad = (
|
|
5
|
+
platedHole: PcbHoleRotatedPillWithRectPad,
|
|
6
|
+
ctx: ConvertContext,
|
|
7
|
+
): void => {
|
|
8
|
+
// TODO: Implement rotated pill hole with rectangular pad generation
|
|
9
|
+
console.warn(
|
|
10
|
+
`Rotated pill hole with rect pad not yet implemented: ${platedHole.pcb_plated_hole_id}`,
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PcbPlatedHole } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
import { addCirclePlatedHole } from "./addCirclePlatedHole"
|
|
4
|
+
import { addOvalPlatedHole } from "./addOvalPlatedHole"
|
|
5
|
+
import { addCircularHoleWithRectPad } from "./addCircularHoleWithRectPad"
|
|
6
|
+
import { addPillHoleWithRectPad } from "./addPillHoleWithRectPad"
|
|
7
|
+
import { addRotatedPillHoleWithRectPad } from "./addRotatedPillHoleWithRectPad"
|
|
8
|
+
import { addHoleWithPolygonPad } from "./addHoleWithPolygonPad"
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Main dispatcher function that routes plated holes to the appropriate handler
|
|
12
|
+
* based on their shape property
|
|
13
|
+
*/
|
|
14
|
+
export const addPlatedHole = (
|
|
15
|
+
platedHole: PcbPlatedHole,
|
|
16
|
+
ctx: ConvertContext,
|
|
17
|
+
): void => {
|
|
18
|
+
switch (platedHole.shape) {
|
|
19
|
+
case "circle":
|
|
20
|
+
return addCirclePlatedHole(platedHole, ctx)
|
|
21
|
+
|
|
22
|
+
case "oval":
|
|
23
|
+
case "pill":
|
|
24
|
+
return addOvalPlatedHole(platedHole, ctx)
|
|
25
|
+
|
|
26
|
+
case "circular_hole_with_rect_pad":
|
|
27
|
+
return addCircularHoleWithRectPad(platedHole, ctx)
|
|
28
|
+
|
|
29
|
+
case "pill_hole_with_rect_pad":
|
|
30
|
+
return addPillHoleWithRectPad(platedHole, ctx)
|
|
31
|
+
|
|
32
|
+
case "rotated_pill_hole_with_rect_pad":
|
|
33
|
+
return addRotatedPillHoleWithRectPad(platedHole, ctx)
|
|
34
|
+
|
|
35
|
+
case "hole_with_polygon_pad":
|
|
36
|
+
return addHoleWithPolygonPad(platedHole, ctx)
|
|
37
|
+
|
|
38
|
+
default:
|
|
39
|
+
// Type guard to ensure we handle all cases
|
|
40
|
+
const _exhaustive: never = platedHole
|
|
41
|
+
console.warn(`Unknown plated hole shape: ${(platedHole as any).shape}`)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
2
|
+
import type { PcbSmtPadRect } from "circuit-json"
|
|
3
|
+
import { ShapePath } from "lbrnts"
|
|
4
|
+
|
|
5
|
+
export const addRectSmtPad = (smtPad: PcbSmtPadRect, ctx: ConvertContext) => {
|
|
6
|
+
const { project, copperCutSetting } = ctx
|
|
7
|
+
|
|
8
|
+
const centerX = smtPad.x
|
|
9
|
+
const centerY = smtPad.y
|
|
10
|
+
const halfWidth = smtPad.width / 2
|
|
11
|
+
const halfHeight = smtPad.height / 2
|
|
12
|
+
|
|
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
|
+
]
|
|
20
|
+
|
|
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
|
+
)
|
|
37
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PcbSmtPad } from "circuit-json"
|
|
2
|
+
import type { ConvertContext } from "../../ConvertContext"
|
|
3
|
+
import { addRectSmtPad } from "./addRectSmtPad"
|
|
4
|
+
|
|
5
|
+
export const addSmtPad = (smtPad: PcbSmtPad, ctx: ConvertContext) => {
|
|
6
|
+
switch (smtPad.shape) {
|
|
7
|
+
case "rect": {
|
|
8
|
+
addRectSmtPad(smtPad, ctx)
|
|
9
|
+
break
|
|
10
|
+
}
|
|
11
|
+
default: {
|
|
12
|
+
throw new Error(`Unknown smt pad shape: ${smtPad.shape}`)
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
package/lib/index.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { CircuitJson } from "circuit-json"
|
|
2
|
+
import { LightBurnProject, CutSetting } from "lbrnts"
|
|
3
|
+
import { cju } from "@tscircuit/circuit-json-util"
|
|
4
|
+
import type { ConvertContext } from "./ConvertContext"
|
|
5
|
+
import { addPlatedHole } from "./element-handlers/addPlatedHole"
|
|
6
|
+
import { addSmtPad } from "./element-handlers/addSmtPad"
|
|
7
|
+
|
|
8
|
+
export const convertCircuitJsonToLbrn = (
|
|
9
|
+
circuitJson: CircuitJson,
|
|
10
|
+
options: { includeSilkscreen?: boolean } = {},
|
|
11
|
+
): LightBurnProject => {
|
|
12
|
+
const db = cju(circuitJson)
|
|
13
|
+
const project = new LightBurnProject({
|
|
14
|
+
appVersion: "1.7.03",
|
|
15
|
+
formatVersion: "1",
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const copperCutSetting = new CutSetting({
|
|
19
|
+
index: 0,
|
|
20
|
+
name: "Cut Copper",
|
|
21
|
+
numPasses: 12,
|
|
22
|
+
speed: 100,
|
|
23
|
+
})
|
|
24
|
+
project.children.push(copperCutSetting)
|
|
25
|
+
|
|
26
|
+
const ctx: ConvertContext = {
|
|
27
|
+
db,
|
|
28
|
+
project,
|
|
29
|
+
copperCutSetting,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const smtpad of db.pcb_smtpad.list()) {
|
|
33
|
+
addSmtPad(smtpad, ctx)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const platedHole of db.pcb_plated_hole.list()) {
|
|
37
|
+
addPlatedHole(platedHole, ctx)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return project
|
|
41
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "circuit-json-to-lbrn",
|
|
3
|
+
"main": "dist/index.js",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsup-node ./lib/index.ts --dts --format esm",
|
|
8
|
+
"format": "biome format --write .",
|
|
9
|
+
"format:check": "biome format ."
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@biomejs/biome": "^2.3.6",
|
|
13
|
+
"@tscircuit/circuit-json-util": "^0.0.72",
|
|
14
|
+
"@types/bun": "latest",
|
|
15
|
+
"bun-match-svg": "^0.0.14",
|
|
16
|
+
"circuit-json": "^0.0.316",
|
|
17
|
+
"circuit-json-to-connectivity-map": "^0.0.22",
|
|
18
|
+
"circuit-to-svg": "^0.0.271",
|
|
19
|
+
"schematic-symbols": "^0.0.202",
|
|
20
|
+
"stack-svgs": "^0.0.1",
|
|
21
|
+
"tsup": "^8.5.1"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"typescript": "^5"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"lbrnts": "^0.0.1"
|
|
28
|
+
}
|
|
29
|
+
}
|