circuit-json-to-lbrn 0.0.24 → 0.0.25

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/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: 12,
2559
- speed: 100
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: 12,
2566
- speed: 100
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: 3,
2573
- speed: 50
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: 12,
61
- speed: 100,
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: 12,
69
- speed: 100,
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: 3,
77
- speed: 50,
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.24",
4
+ "version": "0.0.25",
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.8"
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
- <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
- />
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
- <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
- />
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
- <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
- />
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
- <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
- />
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
- <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 grid grid-cols-1 md:grid-cols-3 gap-4">
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
+ })