circuit-json-to-kicad 0.0.1 → 0.0.3

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/dist/index.js ADDED
@@ -0,0 +1,1341 @@
1
+ // lib/types.ts
2
+ var ConverterStage = class {
3
+ MAX_ITERATIONS = 1e3;
4
+ iteration = 0;
5
+ finished = false;
6
+ input;
7
+ ctx;
8
+ constructor(input, ctx) {
9
+ this.input = input;
10
+ this.ctx = ctx;
11
+ }
12
+ step() {
13
+ this.iteration++;
14
+ if (this.iteration > this.MAX_ITERATIONS) {
15
+ throw new Error("Max iterations reached");
16
+ }
17
+ this._step();
18
+ }
19
+ _step() {
20
+ throw new Error("Not implemented");
21
+ }
22
+ runUntilFinished() {
23
+ while (!this.finished) {
24
+ this.step();
25
+ }
26
+ }
27
+ getOutput() {
28
+ throw new Error("Not implemented");
29
+ }
30
+ };
31
+
32
+ // lib/schematic/CircuitJsonToKicadSchConverter.ts
33
+ import { KicadSch } from "kicadts";
34
+ import { cju } from "@tscircuit/circuit-json-util";
35
+ import { compose, translate, scale } from "transformation-matrix";
36
+
37
+ // lib/schematic/stages/InitializeSchematicStage.ts
38
+ import { Paper, Uuid } from "kicadts";
39
+ var InitializeSchematicStage = class extends ConverterStage {
40
+ _step() {
41
+ const { kicadSch } = this.ctx;
42
+ if (!kicadSch) {
43
+ throw new Error("KicadSch instance not initialized in context");
44
+ }
45
+ kicadSch.version = 20250114;
46
+ const paper = new Paper();
47
+ paper.size = "A4";
48
+ kicadSch.paper = paper;
49
+ kicadSch.uuid = new Uuid(crypto.randomUUID());
50
+ this.finished = true;
51
+ }
52
+ getOutput() {
53
+ return this.ctx.kicadSch;
54
+ }
55
+ };
56
+
57
+ // lib/schematic/stages/AddLibrarySymbolsStage.ts
58
+ import {
59
+ EmbeddedFonts,
60
+ LibSymbols,
61
+ Pts,
62
+ SchematicSymbol,
63
+ Stroke,
64
+ SymbolPin,
65
+ SymbolPinName,
66
+ SymbolPinNames,
67
+ SymbolPinNumber,
68
+ SymbolPinNumbers,
69
+ SymbolPolyline,
70
+ SymbolPolylineFill,
71
+ SymbolProperty,
72
+ TextEffects,
73
+ TextEffectsFont,
74
+ Xy
75
+ } from "kicadts";
76
+ import { symbols } from "schematic-symbols";
77
+ var AddLibrarySymbolsStage = class extends ConverterStage {
78
+ _step() {
79
+ const { kicadSch, db } = this.ctx;
80
+ const schematicComponents = db.schematic_component.list();
81
+ if (schematicComponents.length === 0) {
82
+ this.finished = true;
83
+ return;
84
+ }
85
+ const libSymbols = new LibSymbols();
86
+ const symbolsToCreate = /* @__PURE__ */ new Set();
87
+ for (const comp of schematicComponents) {
88
+ if (comp.symbol_name) {
89
+ symbolsToCreate.add(comp.symbol_name);
90
+ } else {
91
+ const sourceComp = comp.source_component_id ? db.source_component.get(comp.source_component_id) : null;
92
+ if (sourceComp?.ftype === "simple_chip") {
93
+ symbolsToCreate.add(`generic_chip_${comp.source_component_id}`);
94
+ }
95
+ }
96
+ }
97
+ const librarySymbols = [];
98
+ for (const symbolName of symbolsToCreate) {
99
+ let symbolData = symbols[symbolName];
100
+ let exampleComp;
101
+ let sourceComp;
102
+ if (symbolName.startsWith("generic_chip_")) {
103
+ const sourceCompId = symbolName.replace("generic_chip_", "");
104
+ sourceComp = db.source_component.get(sourceCompId);
105
+ exampleComp = schematicComponents.find(
106
+ (c) => c.source_component_id === sourceCompId
107
+ );
108
+ if (exampleComp) {
109
+ symbolData = this.createGenericChipSymbolData(exampleComp, db);
110
+ }
111
+ } else {
112
+ symbolData = symbols[symbolName];
113
+ if (!symbolData) {
114
+ console.warn(`Symbol ${symbolName} not found in schematic-symbols`);
115
+ continue;
116
+ }
117
+ exampleComp = schematicComponents.find(
118
+ (c) => c.symbol_name === symbolName
119
+ );
120
+ sourceComp = exampleComp && exampleComp.source_component_id ? db.source_component.get(exampleComp.source_component_id) : null;
121
+ }
122
+ const libSymbol = this.createLibrarySymbolFromSchematicSymbol(
123
+ symbolName,
124
+ symbolData,
125
+ sourceComp
126
+ );
127
+ librarySymbols.push(libSymbol);
128
+ }
129
+ libSymbols.symbols = librarySymbols;
130
+ if (kicadSch) {
131
+ kicadSch.libSymbols = libSymbols;
132
+ }
133
+ this.finished = true;
134
+ }
135
+ /**
136
+ * Create generic chip symbol data for chips without a symbol_name
137
+ */
138
+ createGenericChipSymbolData(schematicComp, db) {
139
+ const schematicPorts = db.schematic_port.list().filter(
140
+ (p) => p.schematic_component_id === schematicComp.schematic_component_id
141
+ ).sort((a, b) => (a.pin_number || 0) - (b.pin_number || 0));
142
+ const width = schematicComp.size?.width || 1.5;
143
+ const height = schematicComp.size?.height || 1;
144
+ const boxPath = {
145
+ type: "path",
146
+ points: [
147
+ { x: -width / 2, y: -height / 2 },
148
+ { x: width / 2, y: -height / 2 },
149
+ { x: width / 2, y: height / 2 },
150
+ { x: -width / 2, y: height / 2 },
151
+ { x: -width / 2, y: -height / 2 }
152
+ ]
153
+ };
154
+ const ports = schematicPorts.map((port) => {
155
+ const portX = port.center.x - schematicComp.center.x;
156
+ const portY = port.center.y - schematicComp.center.y;
157
+ return {
158
+ x: portX,
159
+ y: portY,
160
+ labels: [port.display_pin_label || `${port.pin_number || 1}`],
161
+ pinNumber: port.pin_number || 1
162
+ };
163
+ });
164
+ return {
165
+ center: { x: 0, y: 0 },
166
+ primitives: [boxPath],
167
+ ports,
168
+ size: { width, height }
169
+ };
170
+ }
171
+ /**
172
+ * Convert schematic-symbols data to KiCad library symbol
173
+ */
174
+ createLibrarySymbolFromSchematicSymbol(symbolName, symbolData, sourceComp) {
175
+ const libId = this.getLibraryId(symbolName, sourceComp);
176
+ const symbol = new SchematicSymbol({
177
+ libraryId: libId,
178
+ excludeFromSim: false,
179
+ inBom: true,
180
+ onBoard: true
181
+ });
182
+ const pinNumbers = new SymbolPinNumbers();
183
+ pinNumbers.hide = sourceComp?.ftype !== "simple_chip";
184
+ symbol._sxPinNumbers = pinNumbers;
185
+ const pinNames = new SymbolPinNames();
186
+ pinNames.offset = sourceComp?.ftype === "simple_chip" ? 1.27 : 0;
187
+ symbol._sxPinNames = pinNames;
188
+ this.addSymbolProperties(symbol, libId, sourceComp);
189
+ const isChip = sourceComp?.ftype === "simple_chip";
190
+ const drawingSymbol = this.createDrawingSubsymbol(libId, symbolData, isChip);
191
+ symbol.subSymbols.push(drawingSymbol);
192
+ const pinSymbol = this.createPinSubsymbol(libId, symbolData, isChip);
193
+ symbol.subSymbols.push(pinSymbol);
194
+ symbol._sxEmbeddedFonts = new EmbeddedFonts(false);
195
+ return symbol;
196
+ }
197
+ /**
198
+ * Get KiCad library ID for a symbol
199
+ */
200
+ getLibraryId(symbolName, sourceComp) {
201
+ if (sourceComp?.ftype === "simple_resistor") {
202
+ return "Device:R";
203
+ }
204
+ if (sourceComp?.ftype === "simple_capacitor") {
205
+ return "Device:C";
206
+ }
207
+ if (sourceComp?.ftype === "simple_chip") {
208
+ return "Device:U";
209
+ }
210
+ return `Custom:${symbolName}`;
211
+ }
212
+ /**
213
+ * Add properties to the library symbol
214
+ */
215
+ addSymbolProperties(symbol, libId, sourceComp) {
216
+ const refPrefix = libId.split(":")[1]?.[0] || "U";
217
+ const properties = [
218
+ {
219
+ key: "Reference",
220
+ value: refPrefix,
221
+ id: 0,
222
+ at: [2.032, 0, 90],
223
+ hide: false
224
+ },
225
+ { key: "Value", value: refPrefix, id: 1, at: [0, 0, 90], hide: false },
226
+ {
227
+ key: "Footprint",
228
+ value: "",
229
+ id: 2,
230
+ at: [-1.778, 0, 90],
231
+ hide: true
232
+ },
233
+ {
234
+ key: "Datasheet",
235
+ value: "~",
236
+ id: 3,
237
+ at: [0, 0, 0],
238
+ hide: true
239
+ },
240
+ {
241
+ key: "Description",
242
+ value: this.getDescription(sourceComp),
243
+ id: 4,
244
+ at: [0, 0, 0],
245
+ hide: true
246
+ },
247
+ {
248
+ key: "ki_keywords",
249
+ value: this.getKeywords(sourceComp),
250
+ id: 5,
251
+ at: [0, 0, 0],
252
+ hide: true
253
+ },
254
+ {
255
+ key: "ki_fp_filters",
256
+ value: this.getFpFilters(sourceComp),
257
+ id: 6,
258
+ at: [0, 0, 0],
259
+ hide: true
260
+ }
261
+ ];
262
+ for (const prop of properties) {
263
+ symbol.properties.push(
264
+ new SymbolProperty({
265
+ key: prop.key,
266
+ value: prop.value,
267
+ id: prop.id,
268
+ at: prop.at,
269
+ effects: this.createTextEffects(1.27, prop.hide)
270
+ })
271
+ );
272
+ }
273
+ }
274
+ getDescription(sourceComp) {
275
+ if (sourceComp?.ftype === "simple_resistor") return "Resistor";
276
+ if (sourceComp?.ftype === "simple_capacitor") return "Capacitor";
277
+ if (sourceComp?.ftype === "simple_chip") return "Integrated Circuit";
278
+ return "Component";
279
+ }
280
+ getKeywords(sourceComp) {
281
+ if (sourceComp?.ftype === "simple_resistor") return "R res resistor";
282
+ if (sourceComp?.ftype === "simple_capacitor") return "C cap capacitor";
283
+ if (sourceComp?.ftype === "simple_chip") return "U IC chip";
284
+ return "";
285
+ }
286
+ getFpFilters(sourceComp) {
287
+ if (sourceComp?.ftype === "simple_resistor") return "R_*";
288
+ if (sourceComp?.ftype === "simple_capacitor") return "C_*";
289
+ if (sourceComp?.ftype === "simple_chip") return "*";
290
+ return "*";
291
+ }
292
+ /**
293
+ * Create the drawing subsymbol (primitives, no pins)
294
+ * Converts schematic-symbols primitives to KiCad drawing elements
295
+ */
296
+ createDrawingSubsymbol(libId, symbolData, isChip = false) {
297
+ const drawingSymbol = new SchematicSymbol({
298
+ libraryId: `${libId.split(":")[1]}_0_1`
299
+ });
300
+ const symbolScale = this.ctx.c2kMatSch?.a || 15;
301
+ for (const primitive of symbolData.primitives || []) {
302
+ if (primitive.type === "path" && primitive.points) {
303
+ const fillType = isChip ? "background" : "none";
304
+ const polyline = this.createPolylineFromPoints(
305
+ primitive.points,
306
+ symbolScale,
307
+ fillType
308
+ );
309
+ drawingSymbol.polylines.push(polyline);
310
+ }
311
+ }
312
+ return drawingSymbol;
313
+ }
314
+ /**
315
+ * Create a KiCad polyline from points
316
+ */
317
+ createPolylineFromPoints(points, scale3, fillType = "none") {
318
+ const polyline = new SymbolPolyline();
319
+ const xyPoints = points.map((p) => new Xy(p.x * scale3, p.y * scale3));
320
+ const pts = new Pts(xyPoints);
321
+ polyline.points = pts;
322
+ const stroke = new Stroke();
323
+ stroke.width = 0.254;
324
+ stroke.type = "default";
325
+ polyline.stroke = stroke;
326
+ const fill = new SymbolPolylineFill();
327
+ fill.type = fillType;
328
+ polyline.fill = fill;
329
+ return polyline;
330
+ }
331
+ /**
332
+ * Create the pin subsymbol
333
+ */
334
+ createPinSubsymbol(libId, symbolData, isChip = false) {
335
+ const pinSymbol = new SchematicSymbol({
336
+ libraryId: `${libId.split(":")[1]}_1_1`
337
+ });
338
+ for (let i = 0; i < (symbolData.ports?.length || 0); i++) {
339
+ const port = symbolData.ports[i];
340
+ const pin = new SymbolPin();
341
+ pin.pinElectricalType = "passive";
342
+ pin.pinGraphicStyle = "line";
343
+ const { x, y, angle } = this.calculatePinPosition(
344
+ port,
345
+ symbolData.center,
346
+ symbolData.size,
347
+ isChip
348
+ );
349
+ pin.at = [x, y, angle];
350
+ pin.length = isChip ? 2.54 : 1.27;
351
+ const nameFont = new TextEffectsFont();
352
+ nameFont.size = { height: 1.27, width: 1.27 };
353
+ const nameEffects = new TextEffects({ font: nameFont });
354
+ const pinName = port.labels?.[0] || "~";
355
+ pin._sxName = new SymbolPinName({ value: pinName, effects: nameEffects });
356
+ const numFont = new TextEffectsFont();
357
+ numFont.size = { height: 1.27, width: 1.27 };
358
+ const numEffects = new TextEffects({ font: numFont });
359
+ const pinNum = port.pinNumber?.toString() || `${i + 1}`;
360
+ pin._sxNumber = new SymbolPinNumber({
361
+ value: pinNum,
362
+ effects: numEffects
363
+ });
364
+ pinSymbol.pins.push(pin);
365
+ }
366
+ return pinSymbol;
367
+ }
368
+ /**
369
+ * Calculate KiCad pin position and rotation from schematic-symbols port
370
+ * Scale pins to match the c2kMatSch transformation scale
371
+ */
372
+ calculatePinPosition(port, center, size, isChip) {
373
+ const symbolScale = this.ctx.c2kMatSch?.a || 15;
374
+ const dx = port.x - center.x;
375
+ const dy = port.y - center.y;
376
+ let x = port.x * symbolScale;
377
+ let y = port.y * symbolScale;
378
+ const chipPinLength = 2.54;
379
+ if (isChip && size) {
380
+ const halfWidth = size.width / 2 * symbolScale;
381
+ const halfHeight = size.height / 2 * symbolScale;
382
+ if (Math.abs(dx) > Math.abs(dy)) {
383
+ x = dx > 0 ? halfWidth : -halfWidth;
384
+ y = dy * symbolScale;
385
+ } else {
386
+ x = dx * symbolScale;
387
+ y = dy > 0 ? halfHeight : -halfHeight;
388
+ }
389
+ }
390
+ let angle = 0;
391
+ if (Math.abs(dx) > Math.abs(dy)) {
392
+ if (dx > 0) {
393
+ if (isChip) {
394
+ angle = 180;
395
+ x = x + chipPinLength;
396
+ } else {
397
+ angle = 0;
398
+ }
399
+ } else {
400
+ if (isChip) {
401
+ angle = 0;
402
+ x = x - chipPinLength;
403
+ } else {
404
+ angle = 180;
405
+ }
406
+ }
407
+ } else {
408
+ if (dy > 0) {
409
+ if (isChip) {
410
+ angle = 270;
411
+ y = y + chipPinLength;
412
+ } else {
413
+ angle = 90;
414
+ }
415
+ } else {
416
+ if (isChip) {
417
+ angle = 90;
418
+ y = y - chipPinLength;
419
+ } else {
420
+ angle = 270;
421
+ }
422
+ }
423
+ }
424
+ return { x, y, angle };
425
+ }
426
+ /**
427
+ * Creates text effects for properties
428
+ */
429
+ createTextEffects(size, hide) {
430
+ const font = new TextEffectsFont();
431
+ font.size = { height: size, width: size };
432
+ return new TextEffects({
433
+ font,
434
+ hiddenText: hide
435
+ });
436
+ }
437
+ getOutput() {
438
+ if (!this.ctx.kicadSch) {
439
+ throw new Error("kicadSch is not initialized");
440
+ }
441
+ return this.ctx.kicadSch;
442
+ }
443
+ };
444
+
445
+ // lib/schematic/stages/AddSchematicSymbolsStage.ts
446
+ import {
447
+ SchematicSymbol as SchematicSymbol2,
448
+ SymbolLibId,
449
+ SymbolProperty as SymbolProperty2,
450
+ SymbolPin as SymbolPin2,
451
+ SymbolInstances,
452
+ SymbolInstancesProject,
453
+ SymbolInstancePath,
454
+ TextEffects as TextEffects2,
455
+ TextEffectsFont as TextEffectsFont2,
456
+ TextEffectsJustify
457
+ } from "kicadts";
458
+ import { applyToPoint } from "transformation-matrix";
459
+ import { symbols as symbols2 } from "schematic-symbols";
460
+ var AddSchematicSymbolsStage = class extends ConverterStage {
461
+ _step() {
462
+ const { kicadSch, db } = this.ctx;
463
+ const schematicComponents = db.schematic_component.list();
464
+ if (schematicComponents.length === 0) {
465
+ this.finished = true;
466
+ return;
467
+ }
468
+ const symbols3 = [];
469
+ for (const schematicComponent of schematicComponents) {
470
+ const sourceComponent = schematicComponent.source_component_id ? db.source_component.get(schematicComponent.source_component_id) : null;
471
+ if (!sourceComponent) continue;
472
+ if (!this.ctx.c2kMatSch) continue;
473
+ const { x, y } = applyToPoint(this.ctx.c2kMatSch, {
474
+ x: schematicComponent.center.x,
475
+ y: schematicComponent.center.y
476
+ });
477
+ const uuid = crypto.randomUUID();
478
+ const symbol = new SchematicSymbol2({
479
+ at: [x, y, 0],
480
+ unit: 1,
481
+ excludeFromSim: false,
482
+ inBom: true,
483
+ onBoard: true,
484
+ dnp: false,
485
+ uuid,
486
+ fieldsAutoplaced: true
487
+ });
488
+ const libId = this.getLibraryId(sourceComponent);
489
+ const symLibId = new SymbolLibId(libId);
490
+ symbol._sxLibId = symLibId;
491
+ const { reference, value, description } = this.getComponentMetadata(sourceComponent);
492
+ const { refTextPos, valTextPos } = this.getTextPositions(
493
+ schematicComponent,
494
+ { x, y }
495
+ );
496
+ const referenceProperty = new SymbolProperty2({
497
+ key: "Reference",
498
+ value: reference,
499
+ id: 0,
500
+ at: [refTextPos.x, refTextPos.y, 0],
501
+ effects: this.createTextEffects(1.27, false)
502
+ });
503
+ const hideValue = sourceComponent.ftype === "simple_chip";
504
+ const valueProperty = new SymbolProperty2({
505
+ key: "Value",
506
+ value,
507
+ id: 1,
508
+ at: [valTextPos.x, valTextPos.y, 0],
509
+ effects: this.createTextEffects(1.27, hideValue)
510
+ });
511
+ const footprintProperty = new SymbolProperty2({
512
+ key: "Footprint",
513
+ value: "",
514
+ id: 2,
515
+ at: [x - 1.778, y, 90],
516
+ effects: this.createTextEffects(1.27, true)
517
+ });
518
+ const datasheetProperty = new SymbolProperty2({
519
+ key: "Datasheet",
520
+ value: "~",
521
+ id: 3,
522
+ at: [x, y, 0],
523
+ effects: this.createTextEffects(1.27, true)
524
+ });
525
+ const descriptionProperty = new SymbolProperty2({
526
+ key: "Description",
527
+ value: description,
528
+ id: 4,
529
+ at: [x, y, 0],
530
+ effects: this.createTextEffects(1.27, true)
531
+ });
532
+ symbol.properties.push(
533
+ referenceProperty,
534
+ valueProperty,
535
+ footprintProperty,
536
+ datasheetProperty,
537
+ descriptionProperty
538
+ );
539
+ const schematicPorts = db.schematic_port.list().filter(
540
+ (p) => p.schematic_component_id === schematicComponent.schematic_component_id
541
+ ).sort((a, b) => (a.pin_number || 0) - (b.pin_number || 0));
542
+ for (const port of schematicPorts) {
543
+ const pin = new SymbolPin2();
544
+ pin.numberString = `${port.pin_number || 1}`;
545
+ pin.uuid = crypto.randomUUID();
546
+ symbol.pins.push(pin);
547
+ }
548
+ const instances = new SymbolInstances();
549
+ const project = new SymbolInstancesProject("");
550
+ const path = new SymbolInstancePath(`/${kicadSch?.uuid?.value || ""}`);
551
+ path.reference = reference;
552
+ path.unit = 1;
553
+ project.paths.push(path);
554
+ instances.projects.push(project);
555
+ symbol._sxInstances = instances;
556
+ symbols3.push(symbol);
557
+ }
558
+ if (kicadSch) {
559
+ kicadSch.symbols = symbols3;
560
+ }
561
+ this.finished = true;
562
+ }
563
+ /**
564
+ * Get text positions from schematic symbol definition or schematic_text elements
565
+ */
566
+ getTextPositions(schematicComponent, symbolKicadPos) {
567
+ const schematicTexts = this.ctx.db.schematic_text?.list?.()?.filter(
568
+ (t) => t.schematic_component_id === schematicComponent.schematic_component_id
569
+ ) || [];
570
+ const refText = schematicTexts.find((t) => t.text && t.text.length > 0);
571
+ if (refText && this.ctx.c2kMatSch) {
572
+ const refTextPos2 = applyToPoint(this.ctx.c2kMatSch, {
573
+ x: refText.position.x,
574
+ y: refText.position.y
575
+ });
576
+ const valTextPos2 = { x: symbolKicadPos.x, y: symbolKicadPos.y + 6 };
577
+ return { refTextPos: refTextPos2, valTextPos: valTextPos2 };
578
+ }
579
+ const symbolName = schematicComponent.symbol_name;
580
+ const symbol = symbols2[symbolName];
581
+ if (!symbol) {
582
+ return {
583
+ refTextPos: { x: symbolKicadPos.x, y: symbolKicadPos.y - 6 },
584
+ valTextPos: { x: symbolKicadPos.x, y: symbolKicadPos.y + 6 }
585
+ };
586
+ }
587
+ let refTextPrimitive = null;
588
+ let valTextPrimitive = null;
589
+ for (const primitive of symbol.primitives) {
590
+ if (primitive.type === "text") {
591
+ if (primitive.text === "{REF}") {
592
+ refTextPrimitive = primitive;
593
+ } else if (primitive.text === "{VAL}") {
594
+ valTextPrimitive = primitive;
595
+ }
596
+ }
597
+ }
598
+ const refTextPos = refTextPrimitive && this.ctx.c2kMatSch ? applyToPoint(this.ctx.c2kMatSch, {
599
+ x: schematicComponent.center.x + refTextPrimitive.x,
600
+ y: schematicComponent.center.y + refTextPrimitive.y
601
+ }) : { x: symbolKicadPos.x, y: symbolKicadPos.y - 6 };
602
+ const valTextPos = valTextPrimitive && this.ctx.c2kMatSch ? applyToPoint(this.ctx.c2kMatSch, {
603
+ x: schematicComponent.center.x + valTextPrimitive.x,
604
+ y: schematicComponent.center.y + valTextPrimitive.y
605
+ }) : { x: symbolKicadPos.x, y: symbolKicadPos.y + 6 };
606
+ return { refTextPos, valTextPos };
607
+ }
608
+ /**
609
+ * Get KiCad library ID for a component
610
+ */
611
+ getLibraryId(sourceComp) {
612
+ if (sourceComp.ftype === "simple_resistor") {
613
+ return "Device:R";
614
+ }
615
+ if (sourceComp.ftype === "simple_capacitor") {
616
+ return "Device:C";
617
+ }
618
+ if (sourceComp.ftype === "simple_inductor") {
619
+ return "Device:L";
620
+ }
621
+ if (sourceComp.ftype === "simple_diode") {
622
+ return "Device:D";
623
+ }
624
+ if (sourceComp.ftype === "simple_chip") {
625
+ return "Device:U";
626
+ }
627
+ return "Device:Component";
628
+ }
629
+ /**
630
+ * Get component metadata (reference, value, description)
631
+ */
632
+ getComponentMetadata(sourceComp) {
633
+ const name = sourceComp.name || "?";
634
+ if (sourceComp.ftype === "simple_resistor") {
635
+ return {
636
+ reference: name,
637
+ value: sourceComp.display_resistance || "R",
638
+ description: "Resistor"
639
+ };
640
+ }
641
+ if (sourceComp.ftype === "simple_capacitor") {
642
+ return {
643
+ reference: name,
644
+ value: sourceComp.display_capacitance || "C",
645
+ description: "Capacitor"
646
+ };
647
+ }
648
+ if (sourceComp.ftype === "simple_inductor") {
649
+ return {
650
+ reference: name,
651
+ value: sourceComp.display_inductance || "L",
652
+ description: "Inductor"
653
+ };
654
+ }
655
+ if (sourceComp.ftype === "simple_diode") {
656
+ return {
657
+ reference: name,
658
+ value: "D",
659
+ description: "Diode"
660
+ };
661
+ }
662
+ if (sourceComp.ftype === "simple_chip") {
663
+ return {
664
+ reference: name,
665
+ value: name,
666
+ description: "Integrated Circuit"
667
+ };
668
+ }
669
+ return {
670
+ reference: name,
671
+ value: name,
672
+ description: "Component"
673
+ };
674
+ }
675
+ /**
676
+ * Creates text effects for properties
677
+ */
678
+ createTextEffects(size, hide = false, justify) {
679
+ const font = new TextEffectsFont2();
680
+ font.size = { height: size, width: size };
681
+ const justifyObj = justify ? new TextEffectsJustify({ horizontal: justify }) : void 0;
682
+ const effects = new TextEffects2({
683
+ font,
684
+ hiddenText: hide,
685
+ justify: justifyObj
686
+ });
687
+ return effects;
688
+ }
689
+ getOutput() {
690
+ if (!this.ctx.kicadSch) {
691
+ throw new Error("kicadSch is not initialized");
692
+ }
693
+ return this.ctx.kicadSch;
694
+ }
695
+ };
696
+
697
+ // lib/schematic/stages/AddSchematicTracesStage.ts
698
+ import { Wire, Pts as Pts2, Xy as Xy2, Stroke as Stroke2, Junction } from "kicadts";
699
+ import { applyToPoint as applyToPoint2 } from "transformation-matrix";
700
+ var AddSchematicTracesStage = class extends ConverterStage {
701
+ _step() {
702
+ const { kicadSch, db } = this.ctx;
703
+ if (!kicadSch) {
704
+ throw new Error("KicadSch instance not initialized in context");
705
+ }
706
+ const schematicTraces = db.schematic_trace.list();
707
+ if (schematicTraces.length === 0) {
708
+ this.finished = true;
709
+ return;
710
+ }
711
+ const wires = [];
712
+ const junctions = [];
713
+ for (const schematicTrace of schematicTraces) {
714
+ for (const edge of schematicTrace.edges) {
715
+ const wire = this.createWireFromEdge(edge);
716
+ wires.push(wire);
717
+ }
718
+ for (const junction of schematicTrace.junctions) {
719
+ const kicadJunction = this.createJunction(junction);
720
+ junctions.push(kicadJunction);
721
+ }
722
+ }
723
+ kicadSch.wires = wires;
724
+ kicadSch.junctions = junctions;
725
+ this.finished = true;
726
+ }
727
+ /**
728
+ * Create a KiCad wire from a schematic trace edge
729
+ */
730
+ createWireFromEdge(edge) {
731
+ const wire = new Wire();
732
+ if (!this.ctx.c2kMatSch) {
733
+ throw new Error(
734
+ "Schematic transformation matrix not initialized in context"
735
+ );
736
+ }
737
+ const from = applyToPoint2(this.ctx.c2kMatSch, {
738
+ x: edge.from.x,
739
+ y: edge.from.y
740
+ });
741
+ const to = applyToPoint2(this.ctx.c2kMatSch, {
742
+ x: edge.to.x,
743
+ y: edge.to.y
744
+ });
745
+ const x1 = from.x;
746
+ const y1 = from.y;
747
+ const x2 = to.x;
748
+ const y2 = to.y;
749
+ const pts = new Pts2([new Xy2(x1, y1), new Xy2(x2, y2)]);
750
+ wire.points = pts;
751
+ const stroke = new Stroke2();
752
+ stroke.width = 0;
753
+ stroke.type = "default";
754
+ wire.stroke = stroke;
755
+ wire.uuid = crypto.randomUUID();
756
+ return wire;
757
+ }
758
+ /**
759
+ * Create a KiCad junction from a circuit-json junction point
760
+ */
761
+ createJunction(junction) {
762
+ if (!this.ctx.c2kMatSch) {
763
+ throw new Error(
764
+ "Schematic transformation matrix not initialized in context"
765
+ );
766
+ }
767
+ const { x, y } = applyToPoint2(this.ctx.c2kMatSch, {
768
+ x: junction.x,
769
+ y: junction.y
770
+ });
771
+ const kicadJunction = new Junction({
772
+ at: [x, y, 0],
773
+ diameter: 0
774
+ // 0 means use default diameter
775
+ });
776
+ kicadJunction.uuid = crypto.randomUUID();
777
+ return kicadJunction;
778
+ }
779
+ getOutput() {
780
+ return this.ctx.kicadSch;
781
+ }
782
+ };
783
+
784
+ // lib/schematic/stages/AddSheetInstancesStage.ts
785
+ import {
786
+ SheetInstances,
787
+ SheetInstancesRootPath,
788
+ SheetInstancesRootPage,
789
+ EmbeddedFonts as EmbeddedFonts3
790
+ } from "kicadts";
791
+ var AddSheetInstancesStage = class extends ConverterStage {
792
+ _step() {
793
+ const { kicadSch } = this.ctx;
794
+ if (!kicadSch) {
795
+ throw new Error("KicadSch instance not initialized in context");
796
+ }
797
+ const sheetInstances = new SheetInstances();
798
+ const path = new SheetInstancesRootPath();
799
+ path.value = "/";
800
+ const page = new SheetInstancesRootPage("1");
801
+ path.pages = [page];
802
+ sheetInstances.paths = [path];
803
+ kicadSch.sheetInstances = sheetInstances;
804
+ kicadSch.embeddedFonts = new EmbeddedFonts3(false);
805
+ this.finished = true;
806
+ }
807
+ getOutput() {
808
+ return this.ctx.kicadSch;
809
+ }
810
+ };
811
+
812
+ // lib/schematic/CircuitJsonToKicadSchConverter.ts
813
+ var CircuitJsonToKicadSchConverter = class {
814
+ ctx;
815
+ pipeline;
816
+ currentStageIndex = 0;
817
+ finished = false;
818
+ get currentStage() {
819
+ return this.pipeline[this.currentStageIndex];
820
+ }
821
+ constructor(circuitJson) {
822
+ const CIRCUIT_JSON_SCALE_FACTOR = 15;
823
+ const KICAD_CENTER_X = 95.25;
824
+ const KICAD_CENTER_Y = 73.66;
825
+ this.ctx = {
826
+ db: cju(circuitJson),
827
+ circuitJson,
828
+ kicadSch: new KicadSch({
829
+ generator: "circuit-json-to-kicad",
830
+ generatorVersion: "0.0.1"
831
+ }),
832
+ c2kMatSch: compose(
833
+ translate(KICAD_CENTER_X, KICAD_CENTER_Y),
834
+ scale(CIRCUIT_JSON_SCALE_FACTOR, -CIRCUIT_JSON_SCALE_FACTOR)
835
+ )
836
+ };
837
+ this.pipeline = [
838
+ new InitializeSchematicStage(circuitJson, this.ctx),
839
+ new AddLibrarySymbolsStage(circuitJson, this.ctx),
840
+ new AddSchematicSymbolsStage(circuitJson, this.ctx),
841
+ new AddSchematicTracesStage(circuitJson, this.ctx),
842
+ new AddSheetInstancesStage(circuitJson, this.ctx)
843
+ ];
844
+ }
845
+ step() {
846
+ if (!this.currentStage) {
847
+ this.finished = true;
848
+ return;
849
+ }
850
+ this.currentStage.step();
851
+ if (this.currentStage.finished) {
852
+ this.currentStageIndex++;
853
+ }
854
+ }
855
+ runUntilFinished() {
856
+ while (!this.finished) {
857
+ this.step();
858
+ }
859
+ }
860
+ getOutput() {
861
+ return this.ctx.kicadSch;
862
+ }
863
+ /**
864
+ * Get the output as a string
865
+ */
866
+ getOutputString() {
867
+ return this.ctx.kicadSch.getString();
868
+ }
869
+ };
870
+
871
+ // lib/pcb/CircuitJsonToKicadPcbConverter.ts
872
+ import { KicadPcb } from "kicadts";
873
+ import { cju as cju2 } from "@tscircuit/circuit-json-util";
874
+ import { compose as compose2, translate as translate2, scale as scale2 } from "transformation-matrix";
875
+
876
+ // lib/pcb/stages/InitializePcbStage.ts
877
+ import {
878
+ Paper as Paper2,
879
+ PcbLayers,
880
+ PcbLayerDefinition,
881
+ PcbGeneral,
882
+ Setup
883
+ } from "kicadts";
884
+ var InitializePcbStage = class extends ConverterStage {
885
+ _step() {
886
+ const { kicadPcb } = this.ctx;
887
+ if (!kicadPcb) {
888
+ throw new Error("KicadPcb instance not initialized in context");
889
+ }
890
+ kicadPcb.version = 20241229;
891
+ const paper = new Paper2();
892
+ paper.size = "A4";
893
+ kicadPcb.paper = paper;
894
+ const general = new PcbGeneral();
895
+ general.thickness = 1.6;
896
+ kicadPcb.general = general;
897
+ const setup = new Setup();
898
+ setup.padToMaskClearance = 0;
899
+ kicadPcb.setup = setup;
900
+ const layers = new PcbLayers();
901
+ const layerDefinitions = [
902
+ // Copper layers (standard KiCad indices)
903
+ new PcbLayerDefinition({ index: 0, name: "F.Cu", type: "signal" }),
904
+ new PcbLayerDefinition({ index: 2, name: "B.Cu", type: "signal" }),
905
+ // Technical layers
906
+ new PcbLayerDefinition({ index: 9, name: "F.Adhes", type: "user" }),
907
+ new PcbLayerDefinition({ index: 11, name: "B.Adhes", type: "user" }),
908
+ new PcbLayerDefinition({ index: 13, name: "F.Paste", type: "user" }),
909
+ new PcbLayerDefinition({ index: 15, name: "B.Paste", type: "user" }),
910
+ new PcbLayerDefinition({ index: 5, name: "F.SilkS", type: "user" }),
911
+ new PcbLayerDefinition({ index: 7, name: "B.SilkS", type: "user" }),
912
+ new PcbLayerDefinition({ index: 1, name: "F.Mask", type: "user" }),
913
+ new PcbLayerDefinition({ index: 3, name: "B.Mask", type: "user" }),
914
+ // Drawing layers
915
+ new PcbLayerDefinition({ index: 20, name: "Dwgs.User", type: "user" }),
916
+ new PcbLayerDefinition({ index: 21, name: "Cmts.User", type: "user" }),
917
+ new PcbLayerDefinition({ index: 22, name: "Eco1.User", type: "user" }),
918
+ new PcbLayerDefinition({ index: 23, name: "Eco2.User", type: "user" }),
919
+ new PcbLayerDefinition({ index: 24, name: "Edge.Cuts", type: "user" }),
920
+ new PcbLayerDefinition({ index: 25, name: "Margin", type: "user" }),
921
+ // Fabrication layers
922
+ new PcbLayerDefinition({ index: 17, name: "B.CrtYd", type: "user" }),
923
+ new PcbLayerDefinition({ index: 16, name: "F.CrtYd", type: "user" }),
924
+ new PcbLayerDefinition({ index: 19, name: "B.Fab", type: "user" }),
925
+ new PcbLayerDefinition({ index: 18, name: "F.Fab", type: "user" })
926
+ ];
927
+ layers.definitions = layerDefinitions;
928
+ kicadPcb.layers = layers;
929
+ this.finished = true;
930
+ }
931
+ getOutput() {
932
+ return this.ctx.kicadPcb;
933
+ }
934
+ };
935
+
936
+ // lib/pcb/stages/AddNetsStage.ts
937
+ import { PcbNet } from "kicadts";
938
+ var AddNetsStage = class extends ConverterStage {
939
+ _step() {
940
+ const { kicadPcb } = this.ctx;
941
+ if (!kicadPcb) {
942
+ throw new Error("KicadPcb instance not initialized in context");
943
+ }
944
+ if (!this.ctx.pcbNetMap) {
945
+ this.ctx.pcbNetMap = /* @__PURE__ */ new Map();
946
+ }
947
+ const pcbTraces = this.ctx.db.pcb_trace.list();
948
+ const netNames = /* @__PURE__ */ new Set();
949
+ netNames.add("GND");
950
+ for (const trace of pcbTraces) {
951
+ if (trace.route && trace.route.length > 0) {
952
+ const netName = `Net-${trace.pcb_trace_id}`;
953
+ netNames.add(netName);
954
+ }
955
+ }
956
+ let netNumber = 0;
957
+ for (const netName of Array.from(netNames).sort()) {
958
+ const net = new PcbNet(netNumber, netName);
959
+ const nets = kicadPcb.nets;
960
+ nets.push(net);
961
+ kicadPcb.nets = nets;
962
+ this.ctx.pcbNetMap.set(netName, netNumber);
963
+ netNumber++;
964
+ }
965
+ this.finished = true;
966
+ }
967
+ getOutput() {
968
+ return this.ctx.kicadPcb;
969
+ }
970
+ };
971
+
972
+ // lib/pcb/stages/AddFootprintsStage.ts
973
+ import { Footprint, FpText, FootprintPad } from "kicadts";
974
+ import { applyToPoint as applyToPoint3 } from "transformation-matrix";
975
+ var AddFootprintsStage = class extends ConverterStage {
976
+ componentsProcessed = 0;
977
+ pcbComponents = [];
978
+ constructor(input, ctx) {
979
+ super(input, ctx);
980
+ this.pcbComponents = this.ctx.db.pcb_component.list();
981
+ }
982
+ _step() {
983
+ const { kicadPcb, c2kMatPcb } = this.ctx;
984
+ if (!kicadPcb) {
985
+ throw new Error("KicadPcb instance not initialized in context");
986
+ }
987
+ if (!c2kMatPcb) {
988
+ throw new Error("PCB transformation matrix not initialized in context");
989
+ }
990
+ if (this.componentsProcessed >= this.pcbComponents.length) {
991
+ this.finished = true;
992
+ return;
993
+ }
994
+ const component = this.pcbComponents[this.componentsProcessed];
995
+ const sourceComponent = component.source_component_id ? this.ctx.db.source_component.get(component.source_component_id) : null;
996
+ const footprintName = sourceComponent?.ftype || "Unknown";
997
+ const componentName = sourceComponent?.name || `Component_${this.componentsProcessed}`;
998
+ const transformedPos = applyToPoint3(c2kMatPcb, {
999
+ x: component.center.x,
1000
+ y: component.center.y
1001
+ });
1002
+ const footprint = new Footprint({
1003
+ libraryLink: `tscircuit:${footprintName}`,
1004
+ layer: "F.Cu",
1005
+ at: [transformedPos.x, transformedPos.y, component.rotation || 0],
1006
+ uuid: crypto.randomUUID()
1007
+ });
1008
+ const refText = new FpText({
1009
+ type: "reference",
1010
+ text: componentName,
1011
+ position: [0, -1.5, 0],
1012
+ layer: "F.SilkS",
1013
+ uuid: crypto.randomUUID()
1014
+ });
1015
+ const valueText = new FpText({
1016
+ type: "value",
1017
+ text: footprintName,
1018
+ position: [0, 1.5, 0],
1019
+ layer: "F.Fab",
1020
+ uuid: crypto.randomUUID()
1021
+ });
1022
+ const fpTexts = footprint.fpTexts;
1023
+ fpTexts.push(refText);
1024
+ fpTexts.push(valueText);
1025
+ footprint.fpTexts = fpTexts;
1026
+ const pcbPads = this.ctx.db.pcb_smtpad?.list().filter(
1027
+ (pad) => pad.pcb_component_id === component.pcb_component_id
1028
+ ) || [];
1029
+ const fpPads = footprint.fpPads;
1030
+ let padNumber = 1;
1031
+ for (const pcbPad of pcbPads) {
1032
+ if (!("x" in pcbPad && "y" in pcbPad)) {
1033
+ throw new Error("no support for polygon pads (or any pads w/o X/Y) yet");
1034
+ }
1035
+ const relativeX = pcbPad.x - component.center.x;
1036
+ const relativeY = pcbPad.y - component.center.y;
1037
+ const layerMap = {
1038
+ top: "F.Cu",
1039
+ bottom: "B.Cu"
1040
+ };
1041
+ const padLayer = layerMap[pcbPad.layer] || "F.Cu";
1042
+ const padShape = pcbPad.shape === "circle" ? "circle" : "rect";
1043
+ const padSize = pcbPad.shape === "circle" ? [
1044
+ "radius" in pcbPad ? pcbPad.radius * 2 : 0.5,
1045
+ "radius" in pcbPad ? pcbPad.radius * 2 : 0.5
1046
+ ] : [
1047
+ "width" in pcbPad ? pcbPad.width : 0.5,
1048
+ "height" in pcbPad ? pcbPad.height : 0.5
1049
+ ];
1050
+ const pad = new FootprintPad({
1051
+ number: String(padNumber),
1052
+ padType: "smd",
1053
+ shape: padShape,
1054
+ at: [relativeX, relativeY, 0],
1055
+ size: padSize,
1056
+ layers: [
1057
+ `${padLayer}`,
1058
+ `${padLayer === "F.Cu" ? "F" : "B"}.Paste`,
1059
+ `${padLayer === "F.Cu" ? "F" : "B"}.Mask`
1060
+ ],
1061
+ uuid: crypto.randomUUID()
1062
+ });
1063
+ fpPads.push(pad);
1064
+ padNumber++;
1065
+ }
1066
+ footprint.fpPads = fpPads;
1067
+ const footprints = kicadPcb.footprints;
1068
+ footprints.push(footprint);
1069
+ kicadPcb.footprints = footprints;
1070
+ this.componentsProcessed++;
1071
+ }
1072
+ getOutput() {
1073
+ return this.ctx.kicadPcb;
1074
+ }
1075
+ };
1076
+
1077
+ // lib/pcb/stages/AddTracesStage.ts
1078
+ import { Segment, SegmentNet } from "kicadts";
1079
+ import { applyToPoint as applyToPoint4 } from "transformation-matrix";
1080
+ var AddTracesStage = class extends ConverterStage {
1081
+ tracesProcessed = 0;
1082
+ pcbTraces = [];
1083
+ constructor(input, ctx) {
1084
+ super(input, ctx);
1085
+ this.pcbTraces = this.ctx.db.pcb_trace.list();
1086
+ }
1087
+ _step() {
1088
+ const { kicadPcb, c2kMatPcb, pcbNetMap } = this.ctx;
1089
+ if (!kicadPcb) {
1090
+ throw new Error("KicadPcb instance not initialized in context");
1091
+ }
1092
+ if (!c2kMatPcb) {
1093
+ throw new Error("PCB transformation matrix not initialized in context");
1094
+ }
1095
+ if (this.tracesProcessed >= this.pcbTraces.length) {
1096
+ this.finished = true;
1097
+ return;
1098
+ }
1099
+ const trace = this.pcbTraces[this.tracesProcessed];
1100
+ if (!trace.route || trace.route.length < 2) {
1101
+ this.tracesProcessed++;
1102
+ return;
1103
+ }
1104
+ for (let i = 0; i < trace.route.length - 1; i++) {
1105
+ const startPoint = trace.route[i];
1106
+ const endPoint = trace.route[i + 1];
1107
+ const transformedStart = applyToPoint4(c2kMatPcb, {
1108
+ x: startPoint.x,
1109
+ y: startPoint.y
1110
+ });
1111
+ const transformedEnd = applyToPoint4(c2kMatPcb, {
1112
+ x: endPoint.x,
1113
+ y: endPoint.y
1114
+ });
1115
+ let netNumber = 0;
1116
+ if (pcbNetMap) {
1117
+ const netName = `Net-${trace.pcb_trace_id}`;
1118
+ netNumber = pcbNetMap.get(netName) ?? 0;
1119
+ }
1120
+ const layerMap = {
1121
+ top: "F.Cu",
1122
+ bottom: "B.Cu"
1123
+ };
1124
+ const kicadLayer = layerMap[startPoint.layer] || startPoint.layer || "F.Cu";
1125
+ const segment = new Segment({
1126
+ start: { x: transformedStart.x, y: transformedStart.y },
1127
+ end: { x: transformedEnd.x, y: transformedEnd.y },
1128
+ layer: kicadLayer,
1129
+ width: trace.width || 0.25,
1130
+ net: new SegmentNet(netNumber)
1131
+ });
1132
+ const segments = kicadPcb.segments;
1133
+ segments.push(segment);
1134
+ kicadPcb.segments = segments;
1135
+ }
1136
+ this.tracesProcessed++;
1137
+ }
1138
+ getOutput() {
1139
+ return this.ctx.kicadPcb;
1140
+ }
1141
+ };
1142
+
1143
+ // lib/pcb/stages/AddViasStage.ts
1144
+ import { Via, ViaNet } from "kicadts";
1145
+ import { applyToPoint as applyToPoint5 } from "transformation-matrix";
1146
+ var AddViasStage = class extends ConverterStage {
1147
+ viasProcessed = 0;
1148
+ pcbVias = [];
1149
+ constructor(input, ctx) {
1150
+ super(input, ctx);
1151
+ this.pcbVias = this.ctx.db.pcb_via?.list() || [];
1152
+ }
1153
+ _step() {
1154
+ const { kicadPcb, c2kMatPcb, pcbNetMap } = this.ctx;
1155
+ if (!kicadPcb) {
1156
+ throw new Error("KicadPcb instance not initialized in context");
1157
+ }
1158
+ if (!c2kMatPcb) {
1159
+ throw new Error("PCB transformation matrix not initialized in context");
1160
+ }
1161
+ if (this.viasProcessed >= this.pcbVias.length) {
1162
+ this.finished = true;
1163
+ return;
1164
+ }
1165
+ const via = this.pcbVias[this.viasProcessed];
1166
+ const transformedPos = applyToPoint5(c2kMatPcb, {
1167
+ x: via.x,
1168
+ y: via.y
1169
+ });
1170
+ let netNumber = 0;
1171
+ if (pcbNetMap && via.net_name) {
1172
+ netNumber = pcbNetMap.get(via.net_name) ?? 0;
1173
+ }
1174
+ const kicadVia = new Via({
1175
+ at: [transformedPos.x, transformedPos.y],
1176
+ size: via.outer_diameter || 0.8,
1177
+ drill: via.hole_diameter || 0.4,
1178
+ layers: ["F.Cu", "B.Cu"],
1179
+ net: new ViaNet(netNumber)
1180
+ });
1181
+ const vias = kicadPcb.vias;
1182
+ vias.push(kicadVia);
1183
+ kicadPcb.vias = vias;
1184
+ this.viasProcessed++;
1185
+ }
1186
+ getOutput() {
1187
+ return this.ctx.kicadPcb;
1188
+ }
1189
+ };
1190
+
1191
+ // lib/pcb/stages/AddGraphicsStage.ts
1192
+ import { GrLine } from "kicadts";
1193
+ import { applyToPoint as applyToPoint6 } from "transformation-matrix";
1194
+ var AddGraphicsStage = class extends ConverterStage {
1195
+ _step() {
1196
+ const { kicadPcb, c2kMatPcb } = this.ctx;
1197
+ if (!kicadPcb) {
1198
+ throw new Error("KicadPcb instance not initialized in context");
1199
+ }
1200
+ if (!c2kMatPcb) {
1201
+ throw new Error("PCB transformation matrix not initialized in context");
1202
+ }
1203
+ const pcbSilkscreenPaths = this.ctx.db.pcb_silkscreen_path?.list() || [];
1204
+ for (const path of pcbSilkscreenPaths) {
1205
+ if (!path.route || path.route.length < 2) continue;
1206
+ for (let i = 0; i < path.route.length - 1; i++) {
1207
+ const startPoint = path.route[i];
1208
+ const endPoint = path.route[i + 1];
1209
+ if (!startPoint || !endPoint) continue;
1210
+ const transformedStart = applyToPoint6(c2kMatPcb, {
1211
+ x: startPoint.x,
1212
+ y: startPoint.y
1213
+ });
1214
+ const transformedEnd = applyToPoint6(c2kMatPcb, {
1215
+ x: endPoint.x,
1216
+ y: endPoint.y
1217
+ });
1218
+ const layerMap = {
1219
+ top: "F.SilkS",
1220
+ bottom: "B.SilkS"
1221
+ };
1222
+ const kicadLayer = layerMap[path.layer] || path.layer || "F.SilkS";
1223
+ const grLine = new GrLine({
1224
+ start: { x: transformedStart.x, y: transformedStart.y },
1225
+ end: { x: transformedEnd.x, y: transformedEnd.y },
1226
+ layer: kicadLayer,
1227
+ width: path.stroke_width || 0.15
1228
+ });
1229
+ const graphicLines = kicadPcb.graphicLines;
1230
+ graphicLines.push(grLine);
1231
+ kicadPcb.graphicLines = graphicLines;
1232
+ }
1233
+ }
1234
+ const pcbBoards = this.ctx.db.pcb_board?.list() || [];
1235
+ if (pcbBoards.length > 0) {
1236
+ const board = pcbBoards[0];
1237
+ if (!board) {
1238
+ this.finished = true;
1239
+ return;
1240
+ }
1241
+ let corners;
1242
+ if (board.outline && board.outline.length > 0) {
1243
+ corners = board.outline;
1244
+ } else {
1245
+ const halfWidth = board.width / 2;
1246
+ const halfHeight = board.height / 2;
1247
+ corners = [
1248
+ { x: board.center.x - halfWidth, y: board.center.y - halfHeight },
1249
+ { x: board.center.x + halfWidth, y: board.center.y - halfHeight },
1250
+ { x: board.center.x + halfWidth, y: board.center.y + halfHeight },
1251
+ { x: board.center.x - halfWidth, y: board.center.y + halfHeight }
1252
+ ];
1253
+ }
1254
+ const transformedCorners = corners.map(
1255
+ (corner) => applyToPoint6(c2kMatPcb, corner)
1256
+ );
1257
+ for (let i = 0; i < transformedCorners.length; i++) {
1258
+ const start = transformedCorners[i];
1259
+ const end = transformedCorners[(i + 1) % transformedCorners.length];
1260
+ if (!start || !end) continue;
1261
+ const edgeLine = new GrLine({
1262
+ start: { x: start.x, y: start.y },
1263
+ end: { x: end.x, y: end.y },
1264
+ layer: "Edge.Cuts",
1265
+ width: 0.1
1266
+ });
1267
+ const graphicLines = kicadPcb.graphicLines;
1268
+ graphicLines.push(edgeLine);
1269
+ kicadPcb.graphicLines = graphicLines;
1270
+ }
1271
+ }
1272
+ this.finished = true;
1273
+ }
1274
+ getOutput() {
1275
+ return this.ctx.kicadPcb;
1276
+ }
1277
+ };
1278
+
1279
+ // lib/pcb/CircuitJsonToKicadPcbConverter.ts
1280
+ var CircuitJsonToKicadPcbConverter = class {
1281
+ ctx;
1282
+ pipeline;
1283
+ currentStageIndex = 0;
1284
+ finished = false;
1285
+ get currentStage() {
1286
+ return this.pipeline[this.currentStageIndex];
1287
+ }
1288
+ constructor(circuitJson) {
1289
+ const CIRCUIT_JSON_TO_MM_SCALE = 1;
1290
+ const KICAD_PCB_CENTER_X = 100;
1291
+ const KICAD_PCB_CENTER_Y = 100;
1292
+ this.ctx = {
1293
+ db: cju2(circuitJson),
1294
+ circuitJson,
1295
+ kicadPcb: new KicadPcb({
1296
+ generator: "circuit-json-to-kicad",
1297
+ generatorVersion: "0.0.1"
1298
+ }),
1299
+ c2kMatPcb: compose2(
1300
+ translate2(KICAD_PCB_CENTER_X, KICAD_PCB_CENTER_Y),
1301
+ scale2(CIRCUIT_JSON_TO_MM_SCALE, -CIRCUIT_JSON_TO_MM_SCALE)
1302
+ )
1303
+ };
1304
+ this.pipeline = [
1305
+ new InitializePcbStage(circuitJson, this.ctx),
1306
+ new AddNetsStage(circuitJson, this.ctx),
1307
+ new AddFootprintsStage(circuitJson, this.ctx),
1308
+ new AddTracesStage(circuitJson, this.ctx),
1309
+ new AddViasStage(circuitJson, this.ctx),
1310
+ new AddGraphicsStage(circuitJson, this.ctx)
1311
+ ];
1312
+ }
1313
+ step() {
1314
+ if (!this.currentStage) {
1315
+ this.finished = true;
1316
+ return;
1317
+ }
1318
+ this.currentStage.step();
1319
+ if (this.currentStage.finished) {
1320
+ this.currentStageIndex++;
1321
+ }
1322
+ }
1323
+ runUntilFinished() {
1324
+ while (!this.finished) {
1325
+ this.step();
1326
+ }
1327
+ }
1328
+ getOutput() {
1329
+ return this.ctx.kicadPcb;
1330
+ }
1331
+ /**
1332
+ * Get the output as a string
1333
+ */
1334
+ getOutputString() {
1335
+ return this.ctx.kicadPcb.getString();
1336
+ }
1337
+ };
1338
+ export {
1339
+ CircuitJsonToKicadPcbConverter,
1340
+ CircuitJsonToKicadSchConverter
1341
+ };