circuit-json-to-kicad 0.0.15 → 0.0.17
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 +197 -50
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -823,7 +823,8 @@ import {
|
|
|
823
823
|
SymbolInstancePath as SymbolInstancePath2,
|
|
824
824
|
TextEffects as TextEffects3,
|
|
825
825
|
TextEffectsFont as TextEffectsFont3,
|
|
826
|
-
|
|
826
|
+
TextEffectsJustify as TextEffectsJustify2,
|
|
827
|
+
GlobalLabel
|
|
827
828
|
} from "kicadts";
|
|
828
829
|
import { applyToPoint as applyToPoint3 } from "transformation-matrix";
|
|
829
830
|
var AddSchematicNetLabelsStage = class extends ConverterStage {
|
|
@@ -839,7 +840,7 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
|
|
|
839
840
|
return;
|
|
840
841
|
}
|
|
841
842
|
const symbols3 = [];
|
|
842
|
-
const
|
|
843
|
+
const globalLabels = [];
|
|
843
844
|
for (const netLabel of netLabels) {
|
|
844
845
|
const labelText = netLabel.text || "";
|
|
845
846
|
const symbolName = netLabel.symbol_name;
|
|
@@ -853,9 +854,9 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
|
|
|
853
854
|
symbols3.push(symbol);
|
|
854
855
|
}
|
|
855
856
|
} else {
|
|
856
|
-
const label = this.
|
|
857
|
+
const label = this.createGlobalLabel(netLabel, labelText);
|
|
857
858
|
if (label) {
|
|
858
|
-
|
|
859
|
+
globalLabels.push(label);
|
|
859
860
|
}
|
|
860
861
|
}
|
|
861
862
|
}
|
|
@@ -863,20 +864,24 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
|
|
|
863
864
|
const existingSymbols = kicadSch.symbols || [];
|
|
864
865
|
kicadSch.symbols = [...existingSymbols, ...symbols3];
|
|
865
866
|
}
|
|
866
|
-
if (kicadSch &&
|
|
867
|
-
kicadSch.
|
|
867
|
+
if (kicadSch && globalLabels.length > 0) {
|
|
868
|
+
kicadSch.globalLabels = [
|
|
869
|
+
...kicadSch.globalLabels || [],
|
|
870
|
+
...globalLabels
|
|
871
|
+
];
|
|
868
872
|
}
|
|
869
873
|
this.finished = true;
|
|
870
874
|
}
|
|
871
875
|
/**
|
|
872
876
|
* Create a KiCad symbol instance from a net label with a symbol_name
|
|
873
|
-
* These are treated like
|
|
877
|
+
* These are treated like power/ground symbols (e.g., vcc_up, ground_down)
|
|
878
|
+
* Uses anchor_position as the primary coordinate source
|
|
874
879
|
*/
|
|
875
880
|
createSymbolFromNetLabel(netLabel, labelText, symbolName) {
|
|
876
881
|
if (!this.ctx.c2kMatSch) return null;
|
|
877
882
|
const { x, y } = applyToPoint3(this.ctx.c2kMatSch, {
|
|
878
|
-
x: netLabel.
|
|
879
|
-
y: netLabel.
|
|
883
|
+
x: netLabel.anchor_position?.x ?? netLabel.center?.x ?? 0,
|
|
884
|
+
y: netLabel.anchor_position?.y ?? netLabel.center?.y ?? 0
|
|
880
885
|
});
|
|
881
886
|
const uuid = crypto.randomUUID();
|
|
882
887
|
const symbol = new SchematicSymbol3({
|
|
@@ -954,21 +959,50 @@ var AddSchematicNetLabelsStage = class extends ConverterStage {
|
|
|
954
959
|
return symbol;
|
|
955
960
|
}
|
|
956
961
|
/**
|
|
957
|
-
* Create a KiCad label from a schematic_net_label without a
|
|
962
|
+
* Create a KiCad global label from a schematic_net_label without a symbol_name
|
|
963
|
+
* Uses anchor_position as the primary coordinate source for the arrow anchor point
|
|
958
964
|
*/
|
|
959
|
-
|
|
960
|
-
if (!this.ctx.c2kMatSch) return null;
|
|
965
|
+
createGlobalLabel(netLabel, labelText) {
|
|
966
|
+
if (!this.ctx.c2kMatSch || !this.ctx.kicadSch) return null;
|
|
961
967
|
const { x, y } = applyToPoint3(this.ctx.c2kMatSch, {
|
|
962
|
-
x: netLabel.
|
|
963
|
-
y: netLabel.
|
|
968
|
+
x: netLabel.anchor_position?.x ?? netLabel.center?.x ?? 0,
|
|
969
|
+
y: netLabel.anchor_position?.y ?? netLabel.center?.y ?? 0
|
|
964
970
|
});
|
|
965
|
-
const
|
|
971
|
+
const anchorSide = netLabel.anchor_side || "left";
|
|
972
|
+
const angleMap = {
|
|
973
|
+
left: 0,
|
|
974
|
+
// Anchor on left, arrow points right
|
|
975
|
+
right: 180,
|
|
976
|
+
// Anchor on right, arrow points left
|
|
977
|
+
top: 270,
|
|
978
|
+
// Anchor on top, arrow points down
|
|
979
|
+
bottom: 90
|
|
980
|
+
// Anchor on bottom, arrow points up
|
|
981
|
+
};
|
|
982
|
+
const angle = angleMap[anchorSide] || 0;
|
|
983
|
+
const justifyMap = {
|
|
984
|
+
left: { horizontal: "left" },
|
|
985
|
+
// Anchor on left, text on left
|
|
986
|
+
right: { horizontal: "right" },
|
|
987
|
+
// Anchor on right, text on right
|
|
988
|
+
top: { vertical: "top" },
|
|
989
|
+
// Anchor on top, text on top
|
|
990
|
+
bottom: { vertical: "bottom" }
|
|
991
|
+
// Anchor on bottom, text on bottom
|
|
992
|
+
};
|
|
993
|
+
const justify = justifyMap[anchorSide] || {};
|
|
994
|
+
const effects = this.createTextEffects(1.27, false);
|
|
995
|
+
if (Object.keys(justify).length > 0) {
|
|
996
|
+
effects.justify = new TextEffectsJustify2(justify);
|
|
997
|
+
}
|
|
998
|
+
const globalLabel = new GlobalLabel({
|
|
966
999
|
value: labelText,
|
|
967
|
-
at: [x, y,
|
|
968
|
-
effects
|
|
969
|
-
uuid: crypto.randomUUID()
|
|
1000
|
+
at: [x, y, angle],
|
|
1001
|
+
effects,
|
|
1002
|
+
uuid: crypto.randomUUID(),
|
|
1003
|
+
fieldsAutoplaced: true
|
|
970
1004
|
});
|
|
971
|
-
return
|
|
1005
|
+
return globalLabel;
|
|
972
1006
|
}
|
|
973
1007
|
/**
|
|
974
1008
|
* Creates text effects for properties and labels
|
|
@@ -1315,8 +1349,133 @@ var AddNetsStage = class extends ConverterStage {
|
|
|
1315
1349
|
};
|
|
1316
1350
|
|
|
1317
1351
|
// lib/pcb/stages/AddFootprintsStage.ts
|
|
1318
|
-
import { Footprint, FpText
|
|
1352
|
+
import { Footprint, FpText } from "kicadts";
|
|
1319
1353
|
import { applyToPoint as applyToPoint5 } from "transformation-matrix";
|
|
1354
|
+
|
|
1355
|
+
// lib/pcb/stages/utils/CreateSmdPadFromCircuitJson.ts
|
|
1356
|
+
import { FootprintPad } from "kicadts";
|
|
1357
|
+
function createSmdPadFromCircuitJson({
|
|
1358
|
+
pcbPad,
|
|
1359
|
+
componentCenter,
|
|
1360
|
+
padNumber
|
|
1361
|
+
}) {
|
|
1362
|
+
if (!("x" in pcbPad && "y" in pcbPad)) {
|
|
1363
|
+
throw new Error("no support for polygon pads (or any pads w/o X/Y) yet");
|
|
1364
|
+
}
|
|
1365
|
+
const relativeX = pcbPad.x - componentCenter.x;
|
|
1366
|
+
const relativeY = pcbPad.y - componentCenter.y;
|
|
1367
|
+
const layerMap = {
|
|
1368
|
+
top: "F.Cu",
|
|
1369
|
+
bottom: "B.Cu"
|
|
1370
|
+
};
|
|
1371
|
+
const padLayer = layerMap[pcbPad.layer] || "F.Cu";
|
|
1372
|
+
const padShape = pcbPad.shape === "circle" ? "circle" : "rect";
|
|
1373
|
+
const padSize = pcbPad.shape === "circle" ? [
|
|
1374
|
+
"radius" in pcbPad ? pcbPad.radius * 2 : 0.5,
|
|
1375
|
+
"radius" in pcbPad ? pcbPad.radius * 2 : 0.5
|
|
1376
|
+
] : [
|
|
1377
|
+
"width" in pcbPad ? pcbPad.width : 0.5,
|
|
1378
|
+
"height" in pcbPad ? pcbPad.height : 0.5
|
|
1379
|
+
];
|
|
1380
|
+
return new FootprintPad({
|
|
1381
|
+
number: String(padNumber),
|
|
1382
|
+
padType: "smd",
|
|
1383
|
+
shape: padShape,
|
|
1384
|
+
at: [relativeX, relativeY, 0],
|
|
1385
|
+
size: padSize,
|
|
1386
|
+
layers: [
|
|
1387
|
+
`${padLayer}`,
|
|
1388
|
+
`${padLayer === "F.Cu" ? "F" : "B"}.Paste`,
|
|
1389
|
+
`${padLayer === "F.Cu" ? "F" : "B"}.Mask`
|
|
1390
|
+
],
|
|
1391
|
+
uuid: crypto.randomUUID()
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
// lib/pcb/stages/utils/CreateThruHolePadFromCircuitJson.ts
|
|
1396
|
+
import { FootprintPad as FootprintPad2, PadDrill } from "kicadts";
|
|
1397
|
+
function createThruHolePadFromCircuitJson({
|
|
1398
|
+
platedHole,
|
|
1399
|
+
componentCenter,
|
|
1400
|
+
padNumber
|
|
1401
|
+
}) {
|
|
1402
|
+
if (!("x" in platedHole && "y" in platedHole)) {
|
|
1403
|
+
return null;
|
|
1404
|
+
}
|
|
1405
|
+
const relativeX = platedHole.x - componentCenter.x;
|
|
1406
|
+
const relativeY = platedHole.y - componentCenter.y;
|
|
1407
|
+
let padShape = "circle";
|
|
1408
|
+
let padSize;
|
|
1409
|
+
let drill;
|
|
1410
|
+
let rotation = 0;
|
|
1411
|
+
if (platedHole.shape === "circle") {
|
|
1412
|
+
padShape = "circle";
|
|
1413
|
+
padSize = [platedHole.outer_diameter, platedHole.outer_diameter];
|
|
1414
|
+
drill = new PadDrill({
|
|
1415
|
+
diameter: platedHole.hole_diameter
|
|
1416
|
+
});
|
|
1417
|
+
} else if (platedHole.shape === "pill" || platedHole.shape === "oval") {
|
|
1418
|
+
padShape = "oval";
|
|
1419
|
+
padSize = [
|
|
1420
|
+
platedHole.outer_width,
|
|
1421
|
+
platedHole.outer_height
|
|
1422
|
+
];
|
|
1423
|
+
drill = new PadDrill({
|
|
1424
|
+
oval: true,
|
|
1425
|
+
diameter: platedHole.hole_width,
|
|
1426
|
+
width: platedHole.hole_height
|
|
1427
|
+
});
|
|
1428
|
+
} else if (platedHole.shape === "pill_hole_with_rect_pad") {
|
|
1429
|
+
padShape = "rect";
|
|
1430
|
+
padSize = [
|
|
1431
|
+
platedHole.rect_pad_width,
|
|
1432
|
+
platedHole.rect_pad_height
|
|
1433
|
+
];
|
|
1434
|
+
drill = new PadDrill({
|
|
1435
|
+
oval: true,
|
|
1436
|
+
diameter: platedHole.hole_width,
|
|
1437
|
+
width: platedHole.hole_height
|
|
1438
|
+
});
|
|
1439
|
+
} else if (platedHole.shape === "circular_hole_with_rect_pad") {
|
|
1440
|
+
padShape = "rect";
|
|
1441
|
+
padSize = [
|
|
1442
|
+
platedHole.rect_pad_width,
|
|
1443
|
+
platedHole.rect_pad_height
|
|
1444
|
+
];
|
|
1445
|
+
drill = new PadDrill({
|
|
1446
|
+
diameter: platedHole.hole_diameter
|
|
1447
|
+
});
|
|
1448
|
+
} else if (platedHole.shape === "rotated_pill_hole_with_rect_pad") {
|
|
1449
|
+
padShape = "rect";
|
|
1450
|
+
padSize = [
|
|
1451
|
+
platedHole.rect_pad_width,
|
|
1452
|
+
platedHole.rect_pad_height
|
|
1453
|
+
];
|
|
1454
|
+
drill = new PadDrill({
|
|
1455
|
+
oval: true,
|
|
1456
|
+
diameter: platedHole.hole_width,
|
|
1457
|
+
width: platedHole.hole_height
|
|
1458
|
+
});
|
|
1459
|
+
rotation = platedHole.rect_ccw_rotation || 0;
|
|
1460
|
+
} else {
|
|
1461
|
+
padShape = "circle";
|
|
1462
|
+
padSize = [1.6, 1.6];
|
|
1463
|
+
drill = new PadDrill({ diameter: 0.8 });
|
|
1464
|
+
}
|
|
1465
|
+
return new FootprintPad2({
|
|
1466
|
+
number: String(padNumber),
|
|
1467
|
+
padType: "thru_hole",
|
|
1468
|
+
shape: padShape,
|
|
1469
|
+
at: [relativeX, relativeY, rotation],
|
|
1470
|
+
size: padSize,
|
|
1471
|
+
drill,
|
|
1472
|
+
layers: ["*.Cu", "*.Mask"],
|
|
1473
|
+
removeUnusedLayers: false,
|
|
1474
|
+
uuid: crypto.randomUUID()
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// lib/pcb/stages/AddFootprintsStage.ts
|
|
1320
1479
|
var AddFootprintsStage = class extends ConverterStage {
|
|
1321
1480
|
componentsProcessed = 0;
|
|
1322
1481
|
pcbComponents = [];
|
|
@@ -1374,40 +1533,28 @@ var AddFootprintsStage = class extends ConverterStage {
|
|
|
1374
1533
|
const fpPads = footprint.fpPads;
|
|
1375
1534
|
let padNumber = 1;
|
|
1376
1535
|
for (const pcbPad of pcbPads) {
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
const relativeY = pcbPad.y - component.center.y;
|
|
1382
|
-
const layerMap = {
|
|
1383
|
-
top: "F.Cu",
|
|
1384
|
-
bottom: "B.Cu"
|
|
1385
|
-
};
|
|
1386
|
-
const padLayer = layerMap[pcbPad.layer] || "F.Cu";
|
|
1387
|
-
const padShape = pcbPad.shape === "circle" ? "circle" : "rect";
|
|
1388
|
-
const padSize = pcbPad.shape === "circle" ? [
|
|
1389
|
-
"radius" in pcbPad ? pcbPad.radius * 2 : 0.5,
|
|
1390
|
-
"radius" in pcbPad ? pcbPad.radius * 2 : 0.5
|
|
1391
|
-
] : [
|
|
1392
|
-
"width" in pcbPad ? pcbPad.width : 0.5,
|
|
1393
|
-
"height" in pcbPad ? pcbPad.height : 0.5
|
|
1394
|
-
];
|
|
1395
|
-
const pad = new FootprintPad({
|
|
1396
|
-
number: String(padNumber),
|
|
1397
|
-
padType: "smd",
|
|
1398
|
-
shape: padShape,
|
|
1399
|
-
at: [relativeX, relativeY, 0],
|
|
1400
|
-
size: padSize,
|
|
1401
|
-
layers: [
|
|
1402
|
-
`${padLayer}`,
|
|
1403
|
-
`${padLayer === "F.Cu" ? "F" : "B"}.Paste`,
|
|
1404
|
-
`${padLayer === "F.Cu" ? "F" : "B"}.Mask`
|
|
1405
|
-
],
|
|
1406
|
-
uuid: crypto.randomUUID()
|
|
1536
|
+
const pad = createSmdPadFromCircuitJson({
|
|
1537
|
+
pcbPad,
|
|
1538
|
+
componentCenter: component.center,
|
|
1539
|
+
padNumber
|
|
1407
1540
|
});
|
|
1408
1541
|
fpPads.push(pad);
|
|
1409
1542
|
padNumber++;
|
|
1410
1543
|
}
|
|
1544
|
+
const pcbPlatedHoles = this.ctx.db.pcb_plated_hole?.list().filter(
|
|
1545
|
+
(hole) => hole.pcb_component_id === component.pcb_component_id
|
|
1546
|
+
) || [];
|
|
1547
|
+
for (const platedHole of pcbPlatedHoles) {
|
|
1548
|
+
const pad = createThruHolePadFromCircuitJson({
|
|
1549
|
+
platedHole,
|
|
1550
|
+
componentCenter: component.center,
|
|
1551
|
+
padNumber
|
|
1552
|
+
});
|
|
1553
|
+
if (pad) {
|
|
1554
|
+
fpPads.push(pad);
|
|
1555
|
+
padNumber++;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1411
1558
|
footprint.fpPads = fpPads;
|
|
1412
1559
|
const footprints = kicadPcb.footprints;
|
|
1413
1560
|
footprints.push(footprint);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circuit-json-to-kicad",
|
|
3
3
|
"main": "dist/index.js",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.17",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@types/bun": "latest",
|
|
21
21
|
"circuit-json": "^0.0.267",
|
|
22
22
|
"circuit-to-svg": "^0.0.208",
|
|
23
|
-
"kicadts": "^0.0.
|
|
23
|
+
"kicadts": "^0.0.10",
|
|
24
24
|
"schematic-symbols": "^0.0.202",
|
|
25
25
|
"sharp": "^0.34.4",
|
|
26
26
|
"transformation-matrix": "^3.1.0",
|