circuit-json-to-lbrn 0.0.24 → 0.0.26
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/.github/workflows/bun-test.yml +1 -1
- package/README.md +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +27 -6
- package/lib/index.ts +45 -6
- package/package.json +2 -2
- package/site/index.html +156 -41
- package/site/main.tsx +97 -0
- package/tests/basics/laser-profile.test.ts +91 -0
- package/tests/examples/example01/__snapshots__/example01.snap.svg +1 -0
- package/tests/examples/example01/example01.circuit.json +24366 -0
- package/tests/examples/example01/example01.test.ts +25 -0
- package/tsconfig.json +4 -0
- /package/tests/{examples → basics}/__snapshots__/board-outline-soldermask-preset.snap.svg +0 -0
- /package/tests/{examples → basics}/__snapshots__/board-outline.snap.svg +0 -0
- /package/tests/{examples → basics}/__snapshots__/lga-interconnect.snap.svg +0 -0
- /package/tests/{examples → basics}/__snapshots__/single-trace.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbCutout/__snapshots__/pcb-cutout-circle.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbCutout/__snapshots__/pcb-cutout-path.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbCutout/__snapshots__/pcb-cutout-polygon.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbCutout/__snapshots__/pcb-cutout-rect.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbCutout/pcb-cutout-circle.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbCutout/pcb-cutout-path.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbCutout/pcb-cutout-polygon.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbCutout/pcb-cutout-rect.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbHole/__snapshots__/pcb-hole-circle.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbHole/__snapshots__/pcb-hole-oval.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbHole/__snapshots__/pcb-hole-pill.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbHole/__snapshots__/pcb-hole-rect.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbHole/__snapshots__/pcb-hole-rotated-pill.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbHole/__snapshots__/pcb-hole-with-soldermask.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbHole/pcb-hole-circle.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbHole/pcb-hole-oval.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbHole/pcb-hole-pill.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbHole/pcb-hole-rect.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbHole/pcb-hole-rotated-pill.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbHole/pcb-hole-with-soldermask.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbVia/__snapshots__/pcb-via-basic.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbVia/__snapshots__/pcb-via-with-net.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbVia/__snapshots__/pcb-via-with-soldermask.snap.svg +0 -0
- /package/tests/{examples → basics}/addPcbVia/pcb-via-basic.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbVia/pcb-via-with-net.test.ts +0 -0
- /package/tests/{examples → basics}/addPcbVia/pcb-via-with-soldermask.test.ts +0 -0
- /package/tests/{examples → basics}/addPlatedHole/__snapshots__/pcb-plated-hole-circle.snap.svg +0 -0
- /package/tests/{examples → basics}/addPlatedHole/__snapshots__/pcb-plated-hole-circular-hole-with-rect-pad.snap.svg +0 -0
- /package/tests/{examples → basics}/addPlatedHole/__snapshots__/pcb-plated-hole-oval.snap.svg +0 -0
- /package/tests/{examples → basics}/addPlatedHole/__snapshots__/pcb-plated-hole-pill-with-rect-pad.snap.svg +0 -0
- /package/tests/{examples → basics}/addPlatedHole/__snapshots__/pcb-plated-hole-pill.snap.svg +0 -0
- /package/tests/{examples → basics}/addPlatedHole/__snapshots__/pcb-plated-hole-polygon.snap.svg +0 -0
- /package/tests/{examples → basics}/addPlatedHole/__snapshots__/pcb-plated-hole-rotated-pill-with-rect-pad.snap.svg +0 -0
- /package/tests/{examples → basics}/addPlatedHole/pcb-plated-hole-circle.test.ts +0 -0
- /package/tests/{examples → basics}/addPlatedHole/pcb-plated-hole-circular-hole-with-rect-pad.test.ts +0 -0
- /package/tests/{examples → basics}/addPlatedHole/pcb-plated-hole-oval.test.ts +0 -0
- /package/tests/{examples → basics}/addPlatedHole/pcb-plated-hole-pill-with-rect-pad.test.ts +0 -0
- /package/tests/{examples → basics}/addPlatedHole/pcb-plated-hole-pill.test.ts +0 -0
- /package/tests/{examples → basics}/addPlatedHole/pcb-plated-hole-polygon.test.ts +0 -0
- /package/tests/{examples → basics}/addPlatedHole/pcb-plated-hole-rotated-pill-with-rect-pad.test.ts +0 -0
- /package/tests/{examples → basics}/addSmtPad/__snapshots__/circleSmtPad.snap.svg +0 -0
- /package/tests/{examples → basics}/addSmtPad/__snapshots__/pillSmtPad.snap.svg +0 -0
- /package/tests/{examples → basics}/addSmtPad/__snapshots__/polygonSmtPad.snap.svg +0 -0
- /package/tests/{examples → basics}/addSmtPad/__snapshots__/rotatedPillSmtPad.snap.svg +0 -0
- /package/tests/{examples → basics}/addSmtPad/__snapshots__/rotatedRectSmtPad.snap.svg +0 -0
- /package/tests/{examples → basics}/addSmtPad/circleSmtPad.test.ts +0 -0
- /package/tests/{examples → basics}/addSmtPad/pillSmtPad.test.ts +0 -0
- /package/tests/{examples → basics}/addSmtPad/polygonSmtPad.test.ts +0 -0
- /package/tests/{examples → basics}/addSmtPad/rotatedPillSmtPad.test.ts +0 -0
- /package/tests/{examples → basics}/addSmtPad/rotatedRectSmtPad.test.ts +0 -0
- /package/tests/{examples → basics}/board-outline-soldermask-preset.test.ts +0 -0
- /package/tests/{examples → basics}/board-outline.test.ts +0 -0
- /package/tests/{examples → basics}/keyboard-defaul60/__snapshots__/keyboard-both-layer-includeSoldermask.snap.svg +0 -0
- /package/tests/{examples → basics}/keyboard-defaul60/__snapshots__/keyboard-both-layers.snap.svg +0 -0
- /package/tests/{examples → basics}/keyboard-defaul60/__snapshots__/keyboard-bottom-layer.snap.svg +0 -0
- /package/tests/{examples → basics}/keyboard-defaul60/__snapshots__/keyboard-top-layer.snap.svg +0 -0
- /package/tests/{examples → basics}/keyboard-defaul60/keyboard-both-layer-includeSoldermask.test.ts +0 -0
- /package/tests/{examples → basics}/keyboard-defaul60/keyboard-both-layers.test.ts +0 -0
- /package/tests/{examples → basics}/keyboard-defaul60/keyboard-bottom-layer.test.ts +0 -0
- /package/tests/{examples → basics}/keyboard-defaul60/keyboard-top-layer.test.ts +0 -0
- /package/tests/{examples → basics}/lga-interconnect.test.ts +0 -0
- /package/tests/{examples → basics}/single-trace.test.ts +0 -0
- /package/tests/{examples → basics}/soldermask/__snapshots__/copper-and-soldermask.snap.svg +0 -0
- /package/tests/{examples → basics}/soldermask/__snapshots__/copper-only.snap.svg +0 -0
- /package/tests/{examples → basics}/soldermask/__snapshots__/soldermask-only.snap.svg +0 -0
- /package/tests/{examples → basics}/soldermask/copper-and-soldermask.test.ts +0 -0
- /package/tests/{examples → basics}/soldermask/copper-only.test.ts +0 -0
- /package/tests/{examples → basics}/soldermask/soldermask-only.test.ts +0 -0
- /package/tests/{examples → basics}/soldermask-margin/__snapshots__/negative-soldermask-margin.snap.svg +0 -0
- /package/tests/{examples → basics}/soldermask-margin/__snapshots__/positive-soldermask-margin.snap.svg +0 -0
- /package/tests/{examples → basics}/soldermask-margin/negative-soldermask-margin.test.ts +0 -0
- /package/tests/{examples → basics}/soldermask-margin/positive-soldermask-margin.test.ts +0 -0
- /package/tests/{examples → basics}/trace-margin/__snapshots__/pico-w-3x5-led-matrix-trace-margin.snap.svg +0 -0
- /package/tests/{examples → basics}/trace-margin/__snapshots__/trace-margin-basic.snap.svg +0 -0
- /package/tests/{examples → basics}/trace-margin/pico-w-3x5-led-matrix-trace-margin.test.ts +0 -0
- /package/tests/{examples → basics}/trace-margin/trace-margin-basic.test.ts +0 -0
- /package/tests/{examples → basics}/trace-margin/trace-margin-error.test.ts +0 -0
package/README.md
CHANGED
|
@@ -38,6 +38,7 @@ const defaultLbrn = convertCircuitJsonToLbrn(circuitJson)
|
|
|
38
38
|
- `margin?: number` - Set the margin around the PCB
|
|
39
39
|
- `traceMargin?: number` - Clearance margin around traces in mm (requires `includeCopper: true`)
|
|
40
40
|
- `laserSpotSize?: number` - Laser spot size in mm for crosshatch spacing (default: `0.005`)
|
|
41
|
+
- `laserProfile?: { copper?: { speed?: number; numPasses?: number; frequency?: number; pulseWidth?: number }; board?: { speed?: number; numPasses?: number; frequency?: number; pulseWidth?: number } }` - Custom laser cut settings for copper and board operations. Defaults from GitHub issue: copper (speed: 300 mm/s, numPasses: 100, frequency: 20 kHz, pulseWidth: 1 ns), board (speed: 20 mm/s, numPasses: 100, frequency: 20 kHz, pulseWidth: 1 ns). Allows per-user customization for different lasers/lenses.
|
|
41
42
|
- `includeLayers?: Array<"top" | "bottom">` - Specify which layers to include (default: `["top", "bottom"]`)
|
|
42
43
|
|
|
43
44
|
## Soldermask Support
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,20 @@ declare const convertCircuitJsonToLbrn: (circuitJson: CircuitJson, options?: {
|
|
|
14
14
|
includeLayers?: Array<"top" | "bottom">;
|
|
15
15
|
traceMargin?: number;
|
|
16
16
|
laserSpotSize?: number;
|
|
17
|
+
laserProfile?: {
|
|
18
|
+
copper?: {
|
|
19
|
+
speed?: number;
|
|
20
|
+
numPasses?: number;
|
|
21
|
+
frequency?: number;
|
|
22
|
+
pulseWidth?: number;
|
|
23
|
+
};
|
|
24
|
+
board?: {
|
|
25
|
+
speed?: number;
|
|
26
|
+
numPasses?: number;
|
|
27
|
+
frequency?: number;
|
|
28
|
+
pulseWidth?: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
17
31
|
}) => LightBurnProject;
|
|
18
32
|
|
|
19
33
|
export { convertCircuitJsonToLbrn };
|
package/dist/index.js
CHANGED
|
@@ -2548,6 +2548,21 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
|
2548
2548
|
const includeCopper = options.includeCopper ?? true;
|
|
2549
2549
|
const includeSoldermask = options.includeSoldermask ?? false;
|
|
2550
2550
|
const soldermaskMargin = options.soldermaskMargin ?? 0;
|
|
2551
|
+
const laserProfile = options.laserProfile;
|
|
2552
|
+
const defaultCopperSettings = {
|
|
2553
|
+
speed: 300,
|
|
2554
|
+
numPasses: 100,
|
|
2555
|
+
frequency: 2e4,
|
|
2556
|
+
pulseWidth: 1e-9
|
|
2557
|
+
};
|
|
2558
|
+
const defaultBoardSettings = {
|
|
2559
|
+
speed: 20,
|
|
2560
|
+
numPasses: 100,
|
|
2561
|
+
frequency: 2e4,
|
|
2562
|
+
pulseWidth: 1e-9
|
|
2563
|
+
};
|
|
2564
|
+
const copperSettings = { ...defaultCopperSettings, ...laserProfile?.copper };
|
|
2565
|
+
const boardSettings = { ...defaultBoardSettings, ...laserProfile?.board };
|
|
2551
2566
|
const shouldGenerateTraceClearanceZones = traceMargin > 0 && includeCopper;
|
|
2552
2567
|
if (traceMargin > 0 && !includeCopper) {
|
|
2553
2568
|
throw new Error("traceMargin requires includeCopper to be true");
|
|
@@ -2555,22 +2570,28 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
|
2555
2570
|
const topCopperCutSetting = new CutSetting({
|
|
2556
2571
|
index: 0,
|
|
2557
2572
|
name: "Cut Top Copper",
|
|
2558
|
-
numPasses:
|
|
2559
|
-
speed:
|
|
2573
|
+
numPasses: copperSettings.numPasses,
|
|
2574
|
+
speed: copperSettings.speed,
|
|
2575
|
+
frequency: copperSettings.frequency,
|
|
2576
|
+
pulseWidth: copperSettings.pulseWidth
|
|
2560
2577
|
});
|
|
2561
2578
|
project.children.push(topCopperCutSetting);
|
|
2562
2579
|
const bottomCopperCutSetting = new CutSetting({
|
|
2563
2580
|
index: 1,
|
|
2564
2581
|
name: "Cut Bottom Copper",
|
|
2565
|
-
numPasses:
|
|
2566
|
-
speed:
|
|
2582
|
+
numPasses: copperSettings.numPasses,
|
|
2583
|
+
speed: copperSettings.speed,
|
|
2584
|
+
frequency: copperSettings.frequency,
|
|
2585
|
+
pulseWidth: copperSettings.pulseWidth
|
|
2567
2586
|
});
|
|
2568
2587
|
project.children.push(bottomCopperCutSetting);
|
|
2569
2588
|
const throughBoardCutSetting = new CutSetting({
|
|
2570
2589
|
index: 2,
|
|
2571
2590
|
name: "Cut Through Board",
|
|
2572
|
-
numPasses:
|
|
2573
|
-
speed:
|
|
2591
|
+
numPasses: boardSettings.numPasses,
|
|
2592
|
+
speed: boardSettings.speed,
|
|
2593
|
+
frequency: boardSettings.frequency,
|
|
2594
|
+
pulseWidth: boardSettings.pulseWidth
|
|
2574
2595
|
});
|
|
2575
2596
|
project.children.push(throughBoardCutSetting);
|
|
2576
2597
|
const soldermaskCutSetting = new CutSetting({
|
package/lib/index.ts
CHANGED
|
@@ -29,6 +29,20 @@ export const convertCircuitJsonToLbrn = (
|
|
|
29
29
|
includeLayers?: Array<"top" | "bottom">
|
|
30
30
|
traceMargin?: number
|
|
31
31
|
laserSpotSize?: number
|
|
32
|
+
laserProfile?: {
|
|
33
|
+
copper?: {
|
|
34
|
+
speed?: number
|
|
35
|
+
numPasses?: number
|
|
36
|
+
frequency?: number
|
|
37
|
+
pulseWidth?: number
|
|
38
|
+
}
|
|
39
|
+
board?: {
|
|
40
|
+
speed?: number
|
|
41
|
+
numPasses?: number
|
|
42
|
+
frequency?: number
|
|
43
|
+
pulseWidth?: number
|
|
44
|
+
}
|
|
45
|
+
}
|
|
32
46
|
} = {},
|
|
33
47
|
): LightBurnProject => {
|
|
34
48
|
const db = cju(circuitJson)
|
|
@@ -44,6 +58,25 @@ export const convertCircuitJsonToLbrn = (
|
|
|
44
58
|
const includeCopper = options.includeCopper ?? true
|
|
45
59
|
const includeSoldermask = options.includeSoldermask ?? false
|
|
46
60
|
const soldermaskMargin = options.soldermaskMargin ?? 0
|
|
61
|
+
const laserProfile = options.laserProfile
|
|
62
|
+
|
|
63
|
+
// Default laser settings from GitHub issue
|
|
64
|
+
const defaultCopperSettings = {
|
|
65
|
+
speed: 300,
|
|
66
|
+
numPasses: 100,
|
|
67
|
+
frequency: 20000,
|
|
68
|
+
pulseWidth: 1e-9,
|
|
69
|
+
}
|
|
70
|
+
const defaultBoardSettings = {
|
|
71
|
+
speed: 20,
|
|
72
|
+
numPasses: 100,
|
|
73
|
+
frequency: 20000,
|
|
74
|
+
pulseWidth: 1e-9,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Merge user settings with defaults
|
|
78
|
+
const copperSettings = { ...defaultCopperSettings, ...laserProfile?.copper }
|
|
79
|
+
const boardSettings = { ...defaultBoardSettings, ...laserProfile?.board }
|
|
47
80
|
|
|
48
81
|
// Determine if we should generate trace clearance zones
|
|
49
82
|
const shouldGenerateTraceClearanceZones = traceMargin > 0 && includeCopper
|
|
@@ -57,24 +90,30 @@ export const convertCircuitJsonToLbrn = (
|
|
|
57
90
|
const topCopperCutSetting = new CutSetting({
|
|
58
91
|
index: 0,
|
|
59
92
|
name: "Cut Top Copper",
|
|
60
|
-
numPasses:
|
|
61
|
-
speed:
|
|
93
|
+
numPasses: copperSettings.numPasses,
|
|
94
|
+
speed: copperSettings.speed,
|
|
95
|
+
frequency: copperSettings.frequency,
|
|
96
|
+
pulseWidth: copperSettings.pulseWidth,
|
|
62
97
|
})
|
|
63
98
|
project.children.push(topCopperCutSetting)
|
|
64
99
|
|
|
65
100
|
const bottomCopperCutSetting = new CutSetting({
|
|
66
101
|
index: 1,
|
|
67
102
|
name: "Cut Bottom Copper",
|
|
68
|
-
numPasses:
|
|
69
|
-
speed:
|
|
103
|
+
numPasses: copperSettings.numPasses,
|
|
104
|
+
speed: copperSettings.speed,
|
|
105
|
+
frequency: copperSettings.frequency,
|
|
106
|
+
pulseWidth: copperSettings.pulseWidth,
|
|
70
107
|
})
|
|
71
108
|
project.children.push(bottomCopperCutSetting)
|
|
72
109
|
|
|
73
110
|
const throughBoardCutSetting = new CutSetting({
|
|
74
111
|
index: 2,
|
|
75
112
|
name: "Cut Through Board",
|
|
76
|
-
numPasses:
|
|
77
|
-
speed:
|
|
113
|
+
numPasses: boardSettings.numPasses,
|
|
114
|
+
speed: boardSettings.speed,
|
|
115
|
+
frequency: boardSettings.frequency,
|
|
116
|
+
pulseWidth: boardSettings.pulseWidth,
|
|
78
117
|
})
|
|
79
118
|
project.children.push(throughBoardCutSetting)
|
|
80
119
|
|
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.
|
|
4
|
+
"version": "0.0.26",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"start": "bun run site/index.html",
|
|
@@ -28,6 +28,6 @@
|
|
|
28
28
|
"typescript": "^5"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"lbrnts": "^0.0.
|
|
31
|
+
"lbrnts": "^0.0.9"
|
|
32
32
|
}
|
|
33
33
|
}
|
package/site/index.html
CHANGED
|
@@ -66,63 +66,178 @@
|
|
|
66
66
|
<label class="block text-sm font-medium text-gray-300 mb-2">
|
|
67
67
|
Origin X (mm)
|
|
68
68
|
</label>
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
69
|
+
<input
|
|
70
|
+
type="number"
|
|
71
|
+
id="originX"
|
|
72
|
+
value="0"
|
|
73
|
+
step="0.1"
|
|
74
|
+
class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
75
|
+
/>
|
|
76
76
|
</div>
|
|
77
77
|
<div>
|
|
78
78
|
<label class="block text-sm font-medium text-gray-300 mb-2">
|
|
79
79
|
Origin Y (mm)
|
|
80
80
|
</label>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
<input
|
|
82
|
+
type="number"
|
|
83
|
+
id="originY"
|
|
84
|
+
value="0"
|
|
85
|
+
step="0.1"
|
|
86
|
+
class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
87
|
+
/>
|
|
88
88
|
</div>
|
|
89
89
|
<div>
|
|
90
90
|
<label class="block text-sm font-medium text-gray-300 mb-2">
|
|
91
91
|
Trace Margin (mm)
|
|
92
92
|
</label>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
<input
|
|
94
|
+
type="number"
|
|
95
|
+
id="traceMargin"
|
|
96
|
+
value="0"
|
|
97
|
+
step="0.001"
|
|
98
|
+
class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
99
|
+
/>
|
|
100
100
|
</div>
|
|
101
101
|
<div>
|
|
102
102
|
<label class="block text-sm font-medium text-gray-300 mb-2">
|
|
103
103
|
Soldermask Margin (mm)
|
|
104
104
|
</label>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
<input
|
|
106
|
+
type="number"
|
|
107
|
+
id="soldermaskMargin"
|
|
108
|
+
value="0"
|
|
109
|
+
step="0.001"
|
|
110
|
+
class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
111
|
+
/>
|
|
112
112
|
</div>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
113
|
+
<div>
|
|
114
|
+
<label class="block text-sm font-medium text-gray-300 mb-2">
|
|
115
|
+
Laser Spot Size (mm)
|
|
116
|
+
</label>
|
|
117
|
+
<input
|
|
118
|
+
type="number"
|
|
119
|
+
id="laserSpotSize"
|
|
120
|
+
value="0.005"
|
|
121
|
+
step="0.001"
|
|
122
|
+
class="w-full px-4 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
<div class="md:col-span-2">
|
|
126
|
+
<details class="group">
|
|
127
|
+
<summary class="w-full text-md font-medium text-gray-300 mb-3 flex items-center justify-between bg-blue-500 hover:bg-blue-700 border border-gray-600 rounded-lg px-4 py-3 transition-colors cursor-pointer">
|
|
128
|
+
<span>Laser Cut Settings</span>
|
|
129
|
+
<span class="text-sm text-gray-100 group-open:rotate-90 transition-transform duration-200">▶</span>
|
|
130
|
+
</summary>
|
|
131
|
+
<div class="space-y-4 border border-gray-600 rounded-lg p-4 bg-gray-750">
|
|
132
|
+
<div class="flex items-center justify-between p-3 bg-gray-750 rounded-lg">
|
|
133
|
+
<div class="col-span-2">
|
|
134
|
+
<label class="block text-sm font-medium text-gray-300 mb-2">Preset</label>
|
|
135
|
+
<select id="presetSelect" class="w-full px-3 py-2 bg-gray-700 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
|
|
136
|
+
<option value="default">Default</option>
|
|
137
|
+
<option value="omni-x-6w">Omni X 6W 150x150 - Continuous cutting with conductivity checks</option>
|
|
138
|
+
<!-- Add more options here -->
|
|
139
|
+
</select>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
<table class="w-full text-sm text-gray-300">
|
|
143
|
+
<thead>
|
|
144
|
+
<tr class="border-b border-gray-600">
|
|
145
|
+
<th class="text-left py-2">Layer</th>
|
|
146
|
+
<th class="text-left py-2">Speed (mm/s)</th>
|
|
147
|
+
<th class="text-left py-2">Passes</th>
|
|
148
|
+
<th class="text-left py-2">Frequency (kHz)</th>
|
|
149
|
+
<th class="text-left py-2">Pulse Width (ns)</th>
|
|
150
|
+
</tr>
|
|
151
|
+
</thead>
|
|
152
|
+
<tbody>
|
|
153
|
+
<tr class="border-b border-gray-700">
|
|
154
|
+
<td class="py-2 font-medium">Copper Cut Settings</td>
|
|
155
|
+
<td class="py-2">
|
|
156
|
+
<input
|
|
157
|
+
type="number"
|
|
158
|
+
id="copperSpeed"
|
|
159
|
+
value="300"
|
|
160
|
+
step="1"
|
|
161
|
+
title="Cutting speed for copper layers"
|
|
162
|
+
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
|
163
|
+
/>
|
|
164
|
+
</td>
|
|
165
|
+
<td class="py-2">
|
|
166
|
+
<input
|
|
167
|
+
type="number"
|
|
168
|
+
id="copperNumPasses"
|
|
169
|
+
value="1"
|
|
170
|
+
step="1"
|
|
171
|
+
title="Number of passes for copper ablation"
|
|
172
|
+
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
|
173
|
+
/>
|
|
174
|
+
</td>
|
|
175
|
+
<td class="py-2">
|
|
176
|
+
<input
|
|
177
|
+
type="number"
|
|
178
|
+
id="copperFrequency"
|
|
179
|
+
value="20"
|
|
180
|
+
step="1"
|
|
181
|
+
title="Laser pulse frequency"
|
|
182
|
+
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
|
183
|
+
/>
|
|
184
|
+
</td>
|
|
185
|
+
<td class="py-2">
|
|
186
|
+
<input
|
|
187
|
+
type="number"
|
|
188
|
+
id="copperPulseWidth"
|
|
189
|
+
value="1"
|
|
190
|
+
step="0.1"
|
|
191
|
+
title="Laser pulse width"
|
|
192
|
+
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
|
193
|
+
/>
|
|
194
|
+
</td>
|
|
195
|
+
</tr>
|
|
196
|
+
<tr>
|
|
197
|
+
<td class="py-2 font-medium">Board Laser Settings</td>
|
|
198
|
+
<td class="py-2">
|
|
199
|
+
<input
|
|
200
|
+
type="number"
|
|
201
|
+
id="boardSpeed"
|
|
202
|
+
value="20"
|
|
203
|
+
step="1"
|
|
204
|
+
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
|
205
|
+
/>
|
|
206
|
+
</td>
|
|
207
|
+
<td class="py-2">
|
|
208
|
+
<input
|
|
209
|
+
type="number"
|
|
210
|
+
id="boardNumPasses"
|
|
211
|
+
value="1"
|
|
212
|
+
step="1"
|
|
213
|
+
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
|
214
|
+
/>
|
|
215
|
+
</td>
|
|
216
|
+
<td class="py-2">
|
|
217
|
+
<input
|
|
218
|
+
type="number"
|
|
219
|
+
id="boardFrequency"
|
|
220
|
+
value="20"
|
|
221
|
+
step="1"
|
|
222
|
+
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
|
223
|
+
/>
|
|
224
|
+
</td>
|
|
225
|
+
<td class="py-2">
|
|
226
|
+
<input
|
|
227
|
+
type="number"
|
|
228
|
+
id="boardPulseWidth"
|
|
229
|
+
value="1"
|
|
230
|
+
step="0.1"
|
|
231
|
+
class="w-full px-3 py-2 bg-gray-700 border border-gray-600 text-white focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
|
|
232
|
+
/>
|
|
233
|
+
</td>
|
|
234
|
+
</tr>
|
|
235
|
+
</tbody>
|
|
236
|
+
</table>
|
|
237
|
+
</div>
|
|
238
|
+
</details>
|
|
239
|
+
</div>
|
|
240
|
+
<div class="md:col-span-2 grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
126
241
|
<label class="flex items-center cursor-pointer">
|
|
127
242
|
<input
|
|
128
243
|
type="checkbox"
|
package/site/main.tsx
CHANGED
|
@@ -57,10 +57,81 @@ const includeTopLayerInput = document.getElementById(
|
|
|
57
57
|
const includeBottomLayerInput = document.getElementById(
|
|
58
58
|
"includeBottomLayer",
|
|
59
59
|
) as HTMLInputElement
|
|
60
|
+
const copperSpeedInput = document.getElementById(
|
|
61
|
+
"copperSpeed",
|
|
62
|
+
) as HTMLInputElement
|
|
63
|
+
const copperNumPassesInput = document.getElementById(
|
|
64
|
+
"copperNumPasses",
|
|
65
|
+
) as HTMLInputElement
|
|
66
|
+
const copperFrequencyInput = document.getElementById(
|
|
67
|
+
"copperFrequency",
|
|
68
|
+
) as HTMLInputElement
|
|
69
|
+
const copperPulseWidthInput = document.getElementById(
|
|
70
|
+
"copperPulseWidth",
|
|
71
|
+
) as HTMLInputElement
|
|
72
|
+
const boardSpeedInput = document.getElementById(
|
|
73
|
+
"boardSpeed",
|
|
74
|
+
) as HTMLInputElement
|
|
75
|
+
const boardNumPassesInput = document.getElementById(
|
|
76
|
+
"boardNumPasses",
|
|
77
|
+
) as HTMLInputElement
|
|
78
|
+
const boardFrequencyInput = document.getElementById(
|
|
79
|
+
"boardFrequency",
|
|
80
|
+
) as HTMLInputElement
|
|
81
|
+
const boardPulseWidthInput = document.getElementById(
|
|
82
|
+
"boardPulseWidth",
|
|
83
|
+
) as HTMLInputElement
|
|
84
|
+
const presetSelect = document.getElementById(
|
|
85
|
+
"presetSelect",
|
|
86
|
+
) as HTMLSelectElement
|
|
60
87
|
const reconvertBtn = document.getElementById(
|
|
61
88
|
"reconvertBtn",
|
|
62
89
|
) as HTMLButtonElement
|
|
63
90
|
|
|
91
|
+
// Convert frequency from kHz to Hz
|
|
92
|
+
function kHzToHz(khz: number): number {
|
|
93
|
+
return khz * 1000
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Convert pulse width from ns to seconds
|
|
97
|
+
function nsToSeconds(ns: number): number {
|
|
98
|
+
return ns * 1e-9
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Load Omni X 6W 150x150 preset
|
|
102
|
+
function loadOmniX6W150x150Preset() {
|
|
103
|
+
copperSpeedInput.value = "300"
|
|
104
|
+
copperNumPassesInput.value = "1"
|
|
105
|
+
copperFrequencyInput.value = "20"
|
|
106
|
+
copperPulseWidthInput.value = "1"
|
|
107
|
+
|
|
108
|
+
boardSpeedInput.value = "20"
|
|
109
|
+
boardNumPassesInput.value = "1"
|
|
110
|
+
boardFrequencyInput.value = "20"
|
|
111
|
+
boardPulseWidthInput.value = "1"
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Reset to defaults
|
|
115
|
+
function resetToDefaults() {
|
|
116
|
+
copperSpeedInput.value = "300"
|
|
117
|
+
copperNumPassesInput.value = "1"
|
|
118
|
+
copperFrequencyInput.value = "20"
|
|
119
|
+
copperPulseWidthInput.value = "1"
|
|
120
|
+
|
|
121
|
+
boardSpeedInput.value = "20"
|
|
122
|
+
boardNumPassesInput.value = "1"
|
|
123
|
+
boardFrequencyInput.value = "20"
|
|
124
|
+
boardPulseWidthInput.value = "1"
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Initialize display values on load
|
|
128
|
+
function initializeDisplayValues() {
|
|
129
|
+
copperFrequencyInput.value = "20"
|
|
130
|
+
copperPulseWidthInput.value = "1"
|
|
131
|
+
boardFrequencyInput.value = "20"
|
|
132
|
+
boardPulseWidthInput.value = "1"
|
|
133
|
+
}
|
|
134
|
+
|
|
64
135
|
// Show error message
|
|
65
136
|
function showError(message: string) {
|
|
66
137
|
errorMessage.textContent = message
|
|
@@ -98,6 +169,20 @@ function getConversionOptions() {
|
|
|
98
169
|
],
|
|
99
170
|
traceMargin: parseFloat(traceMarginInput.value) || 0,
|
|
100
171
|
laserSpotSize: parseFloat(laserSpotSizeInput.value) || 0.005,
|
|
172
|
+
laserProfile: {
|
|
173
|
+
copper: {
|
|
174
|
+
speed: parseFloat(copperSpeedInput.value) || 300,
|
|
175
|
+
numPasses: parseInt(copperNumPassesInput.value) || 100,
|
|
176
|
+
frequency: kHzToHz(parseFloat(copperFrequencyInput.value) || 20),
|
|
177
|
+
pulseWidth: nsToSeconds(parseFloat(copperPulseWidthInput.value) || 1),
|
|
178
|
+
},
|
|
179
|
+
board: {
|
|
180
|
+
speed: parseFloat(boardSpeedInput.value) || 20,
|
|
181
|
+
numPasses: parseInt(boardNumPassesInput.value) || 100,
|
|
182
|
+
frequency: kHzToHz(parseFloat(boardFrequencyInput.value) || 20),
|
|
183
|
+
pulseWidth: nsToSeconds(parseFloat(boardPulseWidthInput.value) || 1),
|
|
184
|
+
},
|
|
185
|
+
},
|
|
101
186
|
}
|
|
102
187
|
}
|
|
103
188
|
|
|
@@ -115,6 +200,7 @@ async function processFile(file: File) {
|
|
|
115
200
|
|
|
116
201
|
// Show options container
|
|
117
202
|
optionsContainer.classList.remove("hidden")
|
|
203
|
+
initializeDisplayValues()
|
|
118
204
|
|
|
119
205
|
// Convert to LBRN with options
|
|
120
206
|
await convertAndDisplay()
|
|
@@ -233,6 +319,17 @@ reconvertBtn.addEventListener("click", () => {
|
|
|
233
319
|
convertAndDisplay()
|
|
234
320
|
})
|
|
235
321
|
|
|
322
|
+
// Handle preset select
|
|
323
|
+
presetSelect.addEventListener("change", (e: Event) => {
|
|
324
|
+
const target = e.target as HTMLSelectElement
|
|
325
|
+
const value = target.value
|
|
326
|
+
if (value === "default") {
|
|
327
|
+
resetToDefaults()
|
|
328
|
+
} else if (value === "omni-x-6w") {
|
|
329
|
+
loadOmniX6W150x150Preset()
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
|
|
236
333
|
// Handle download button
|
|
237
334
|
downloadBtn.addEventListener("click", () => {
|
|
238
335
|
if (!currentLbrnProject) {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import { convertCircuitJsonToLbrn } from "../../lib"
|
|
3
|
+
import { CutSetting } from "lbrnts"
|
|
4
|
+
import type { CircuitJson } from "circuit-json"
|
|
5
|
+
|
|
6
|
+
const circuitJson: CircuitJson = [
|
|
7
|
+
{
|
|
8
|
+
type: "pcb_board",
|
|
9
|
+
pcb_board_id: "board1",
|
|
10
|
+
center: { x: 5, y: 5 },
|
|
11
|
+
thickness: 1.6,
|
|
12
|
+
num_layers: 2,
|
|
13
|
+
material: "fr4",
|
|
14
|
+
outline: [
|
|
15
|
+
{ x: 0, y: 0 },
|
|
16
|
+
{ x: 10, y: 0 },
|
|
17
|
+
{ x: 10, y: 10 },
|
|
18
|
+
{ x: 0, y: 10 },
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
test("applies custom laserProfile settings", () => {
|
|
24
|
+
const project = convertCircuitJsonToLbrn(circuitJson, {
|
|
25
|
+
laserProfile: {
|
|
26
|
+
copper: {
|
|
27
|
+
speed: 350,
|
|
28
|
+
numPasses: 150,
|
|
29
|
+
frequency: 25000,
|
|
30
|
+
pulseWidth: 2e-9,
|
|
31
|
+
},
|
|
32
|
+
board: {
|
|
33
|
+
speed: 25,
|
|
34
|
+
numPasses: 120,
|
|
35
|
+
frequency: 21000,
|
|
36
|
+
pulseWidth: 1.5e-9,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
expect(project).toBeDefined()
|
|
42
|
+
const cutSettings = project.children.filter(
|
|
43
|
+
(child) => child.constructor.name === "_CutSetting",
|
|
44
|
+
) as CutSetting[]
|
|
45
|
+
expect(cutSettings.length).toBe(4) // top copper, bottom copper, through board, soldermask
|
|
46
|
+
|
|
47
|
+
// Verify top copper settings
|
|
48
|
+
const topCopper = cutSettings[0]!
|
|
49
|
+
expect(topCopper.speed).toBe(350)
|
|
50
|
+
expect(topCopper.numPasses).toBe(150)
|
|
51
|
+
expect(topCopper.frequency).toBe(25000)
|
|
52
|
+
expect(topCopper.pulseWidth).toBe(2e-9)
|
|
53
|
+
|
|
54
|
+
// Verify bottom copper settings (same as top)
|
|
55
|
+
const bottomCopper = cutSettings[1]!
|
|
56
|
+
expect(bottomCopper.speed).toBe(350)
|
|
57
|
+
expect(bottomCopper.numPasses).toBe(150)
|
|
58
|
+
expect(bottomCopper.frequency).toBe(25000)
|
|
59
|
+
expect(bottomCopper.pulseWidth).toBe(2e-9)
|
|
60
|
+
|
|
61
|
+
// Verify through board settings
|
|
62
|
+
const throughBoard = cutSettings[2]!
|
|
63
|
+
expect(throughBoard.speed).toBe(25)
|
|
64
|
+
expect(throughBoard.numPasses).toBe(120)
|
|
65
|
+
expect(throughBoard.frequency).toBe(21000)
|
|
66
|
+
expect(throughBoard.pulseWidth).toBe(1.5e-9)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test("uses default laserProfile settings when not provided", () => {
|
|
70
|
+
const project = convertCircuitJsonToLbrn(circuitJson)
|
|
71
|
+
|
|
72
|
+
expect(project).toBeDefined()
|
|
73
|
+
const cutSettings = project.children.filter(
|
|
74
|
+
(child) => child.constructor.name === "_CutSetting",
|
|
75
|
+
) as CutSetting[]
|
|
76
|
+
expect(cutSettings.length).toBe(4)
|
|
77
|
+
|
|
78
|
+
// Verify top copper defaults
|
|
79
|
+
const topCopper = cutSettings[0]!
|
|
80
|
+
expect(topCopper.speed).toBe(300)
|
|
81
|
+
expect(topCopper.numPasses).toBe(100)
|
|
82
|
+
expect(topCopper.frequency).toBe(20000)
|
|
83
|
+
expect(topCopper.pulseWidth).toBe(1e-9)
|
|
84
|
+
|
|
85
|
+
// Verify through board defaults
|
|
86
|
+
const throughBoard = cutSettings[2]!
|
|
87
|
+
expect(throughBoard.speed).toBe(20)
|
|
88
|
+
expect(throughBoard.numPasses).toBe(100)
|
|
89
|
+
expect(throughBoard.frequency).toBe(20000)
|
|
90
|
+
expect(throughBoard.pulseWidth).toBe(1e-9)
|
|
91
|
+
})
|