circuit-json-to-kicad 0.0.22 → 0.0.24

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.d.ts CHANGED
@@ -3,6 +3,13 @@ import { CircuitJsonUtilObjects, cju } from '@tscircuit/circuit-json-util';
3
3
  import { KicadSch, KicadPcb } from 'kicadts';
4
4
  import { Matrix } from 'transformation-matrix';
5
5
 
6
+ type PaperSize = "A0" | "A1" | "A2" | "A3" | "A4" | "A5";
7
+ interface PaperDimensions {
8
+ width: number;
9
+ height: number;
10
+ name: PaperSize;
11
+ }
12
+
6
13
  type SchematicPortId = string;
7
14
  type SchematicTraceId = string;
8
15
  type PcbPortId = string;
@@ -15,6 +22,8 @@ interface ConverterContext {
15
22
  c2kMatSch?: Matrix;
16
23
  /** Circuit JSON to KiCad PCB transformation matrix */
17
24
  c2kMatPcb?: Matrix;
25
+ /** Selected paper size for schematic */
26
+ schematicPaperSize?: PaperDimensions;
18
27
  pinPositions?: Map<SchematicPortId, {
19
28
  x: number;
20
29
  y: number;
package/dist/index.js CHANGED
@@ -38,13 +38,13 @@ import { compose, translate, scale } from "transformation-matrix";
38
38
  import { Paper, Uuid } from "kicadts";
39
39
  var InitializeSchematicStage = class extends ConverterStage {
40
40
  _step() {
41
- const { kicadSch } = this.ctx;
41
+ const { kicadSch, schematicPaperSize } = this.ctx;
42
42
  if (!kicadSch) {
43
43
  throw new Error("KicadSch instance not initialized in context");
44
44
  }
45
45
  kicadSch.version = 20250114;
46
46
  const paper = new Paper();
47
- paper.size = "A4";
47
+ paper.size = schematicPaperSize?.name ?? "A4";
48
48
  kicadSch.paper = paper;
49
49
  kicadSch.uuid = new Uuid(crypto.randomUUID());
50
50
  this.finished = true;
@@ -399,14 +399,14 @@ var AddLibrarySymbolsStage = class extends ConverterStage {
399
399
  */
400
400
  createPolylineFromPoints({
401
401
  points,
402
- scale: scale3,
402
+ scale: scale4,
403
403
  center,
404
404
  fillType
405
405
  }) {
406
406
  const polyline = new SymbolPolyline();
407
407
  const cx = center?.x ?? 0;
408
408
  const cy = center?.y ?? 0;
409
- const scaleMatrix = createScaleMatrix(scale3, scale3);
409
+ const scaleMatrix = createScaleMatrix(scale4, scale4);
410
410
  const xyPoints = points.map((p) => {
411
411
  const translated = applyToPoint(scaleMatrix, { x: p.x - cx, y: p.y - cy });
412
412
  return new Xy(translated.x, translated.y);
@@ -1184,6 +1184,26 @@ function getSchematicBoundsAndCenter(db) {
1184
1184
  };
1185
1185
  }
1186
1186
 
1187
+ // lib/schematic/selectSchematicPaperSize.ts
1188
+ var PAPER_SIZES = [
1189
+ { name: "A4", width: 297, height: 210 },
1190
+ { name: "A3", width: 420, height: 297 },
1191
+ { name: "A2", width: 594, height: 420 },
1192
+ { name: "A1", width: 841, height: 594 },
1193
+ { name: "A0", width: 1189, height: 841 }
1194
+ ];
1195
+ function selectSchematicPaperSize(contentWidth, contentHeight, paddingMm = 20) {
1196
+ const requiredWidth = contentWidth + 2 * paddingMm;
1197
+ const requiredHeight = contentHeight + 2 * paddingMm;
1198
+ for (let i = 0; i < PAPER_SIZES.length; i++) {
1199
+ const paperSize = PAPER_SIZES[i];
1200
+ if (requiredWidth <= paperSize.width && requiredHeight <= paperSize.height) {
1201
+ return paperSize;
1202
+ }
1203
+ }
1204
+ return PAPER_SIZES[PAPER_SIZES.length - 1];
1205
+ }
1206
+
1187
1207
  // lib/schematic/CircuitJsonToKicadSchConverter.ts
1188
1208
  var CircuitJsonToKicadSchConverter = class {
1189
1209
  ctx;
@@ -1195,10 +1215,16 @@ var CircuitJsonToKicadSchConverter = class {
1195
1215
  }
1196
1216
  constructor(circuitJson) {
1197
1217
  const CIRCUIT_JSON_SCALE_FACTOR = 15;
1198
- const KICAD_CENTER_Y = 105;
1199
- const KICAD_CENTER_X = 148.5;
1200
1218
  const db = cju(circuitJson);
1201
- const { center } = getSchematicBoundsAndCenter(db);
1219
+ const { center, bounds } = getSchematicBoundsAndCenter(db);
1220
+ const schematicWidthMm = (bounds.maxX - bounds.minX) * CIRCUIT_JSON_SCALE_FACTOR;
1221
+ const schematicHeightMm = (bounds.maxY - bounds.minY) * CIRCUIT_JSON_SCALE_FACTOR;
1222
+ const paperSize = selectSchematicPaperSize(
1223
+ schematicWidthMm,
1224
+ schematicHeightMm
1225
+ );
1226
+ const KICAD_CENTER_X = paperSize.width / 2;
1227
+ const KICAD_CENTER_Y = paperSize.height / 2;
1202
1228
  this.ctx = {
1203
1229
  db,
1204
1230
  circuitJson,
@@ -1206,6 +1232,7 @@ var CircuitJsonToKicadSchConverter = class {
1206
1232
  generator: "circuit-json-to-kicad",
1207
1233
  generatorVersion: "0.0.1"
1208
1234
  }),
1235
+ schematicPaperSize: paperSize,
1209
1236
  c2kMatSch: compose(
1210
1237
  translate(KICAD_CENTER_X, KICAD_CENTER_Y),
1211
1238
  scale(CIRCUIT_JSON_SCALE_FACTOR, -CIRCUIT_JSON_SCALE_FACTOR),
@@ -1250,7 +1277,7 @@ var CircuitJsonToKicadSchConverter = class {
1250
1277
  // lib/pcb/CircuitJsonToKicadPcbConverter.ts
1251
1278
  import { KicadPcb } from "kicadts";
1252
1279
  import { cju as cju2 } from "@tscircuit/circuit-json-util";
1253
- import { compose as compose2, translate as translate2, scale as scale2 } from "transformation-matrix";
1280
+ import { compose as compose3, translate as translate3, scale as scale3 } from "transformation-matrix";
1254
1281
 
1255
1282
  // lib/pcb/stages/InitializePcbStage.ts
1256
1283
  import {
@@ -1353,38 +1380,93 @@ import { Footprint } from "kicadts";
1353
1380
  import { applyToPoint as applyToPoint9 } from "transformation-matrix";
1354
1381
 
1355
1382
  // lib/pcb/stages/utils/CreateSmdPadFromCircuitJson.ts
1356
- import { FootprintPad } from "kicadts";
1357
- import { applyToPoint as applyToPoint5, rotate, identity } from "transformation-matrix";
1383
+ import {
1384
+ FootprintPad,
1385
+ PadPrimitives,
1386
+ PadPrimitiveGrPoly,
1387
+ Pts as Pts3,
1388
+ Xy as Xy3,
1389
+ PadOptions
1390
+ } from "kicadts";
1391
+ import {
1392
+ applyToPoint as applyToPoint5,
1393
+ compose as compose2,
1394
+ translate as translate2,
1395
+ scale as scale2,
1396
+ rotate
1397
+ } from "transformation-matrix";
1358
1398
  function createSmdPadFromCircuitJson({
1359
1399
  pcbPad,
1360
1400
  componentCenter,
1361
1401
  padNumber,
1362
1402
  componentRotation = 0
1363
1403
  }) {
1364
- if (!("x" in pcbPad && "y" in pcbPad)) {
1365
- throw new Error("no support for polygon pads (or any pads w/o X/Y) yet");
1366
- }
1367
- const relativeX = pcbPad.x - componentCenter.x;
1368
- const relativeY = -(pcbPad.y - componentCenter.y);
1369
- const rotationMatrix = componentRotation !== 0 ? rotate(componentRotation * Math.PI / 180) : identity();
1370
- const rotatedPos = applyToPoint5(rotationMatrix, {
1371
- x: relativeX,
1372
- y: relativeY
1404
+ let padX;
1405
+ let padY;
1406
+ if ("x" in pcbPad && "y" in pcbPad) {
1407
+ padX = pcbPad.x;
1408
+ padY = pcbPad.y;
1409
+ } else if ("points" in pcbPad && Array.isArray(pcbPad.points)) {
1410
+ const points = pcbPad.points;
1411
+ padX = points.reduce((sum, p) => sum + p.x, 0) / points.length;
1412
+ padY = points.reduce((sum, p) => sum + p.y, 0) / points.length;
1413
+ } else {
1414
+ throw new Error("Pad must have either x/y coordinates or points array");
1415
+ }
1416
+ const cj2kicadMatrix = compose2(
1417
+ componentRotation !== 0 ? rotate(componentRotation * Math.PI / 180) : { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 },
1418
+ scale2(1, -1),
1419
+ // Flip Y-axis
1420
+ translate2(-componentCenter.x, -componentCenter.y)
1421
+ );
1422
+ const rotatedPos = applyToPoint5(cj2kicadMatrix, {
1423
+ x: padX,
1424
+ y: padY
1373
1425
  });
1374
1426
  const layerMap = {
1375
1427
  top: "F.Cu",
1376
1428
  bottom: "B.Cu"
1377
1429
  };
1378
1430
  const padLayer = layerMap[pcbPad.layer] || "F.Cu";
1379
- const padShape = pcbPad.shape === "circle" ? "circle" : "rect";
1380
- const padSize = pcbPad.shape === "circle" ? [
1381
- "radius" in pcbPad ? pcbPad.radius * 2 : 0.5,
1382
- "radius" in pcbPad ? pcbPad.radius * 2 : 0.5
1383
- ] : [
1384
- "width" in pcbPad ? pcbPad.width : 0.5,
1385
- "height" in pcbPad ? pcbPad.height : 0.5
1386
- ];
1387
- return new FootprintPad({
1431
+ let padShape;
1432
+ let padSize;
1433
+ let padOptions;
1434
+ let padPrimitives;
1435
+ if (pcbPad.shape === "circle") {
1436
+ padShape = "circle";
1437
+ padSize = [
1438
+ "radius" in pcbPad ? pcbPad.radius * 2 : 0.5,
1439
+ "radius" in pcbPad ? pcbPad.radius * 2 : 0.5
1440
+ ];
1441
+ } else if (pcbPad.shape === "polygon" && "points" in pcbPad) {
1442
+ const points = pcbPad.points;
1443
+ const pointTransformMatrix = compose2(
1444
+ scale2(1, -1),
1445
+ // Flip Y-axis
1446
+ translate2(-padX, -padY)
1447
+ );
1448
+ const relativePoints = points.map((p) => {
1449
+ const transformed = applyToPoint5(pointTransformMatrix, { x: p.x, y: p.y });
1450
+ return new Xy3(transformed.x, transformed.y);
1451
+ });
1452
+ const grPoly = new PadPrimitiveGrPoly();
1453
+ grPoly.contours = [new Pts3(relativePoints)];
1454
+ grPoly.width = 0;
1455
+ grPoly.filled = true;
1456
+ padPrimitives = new PadPrimitives();
1457
+ padPrimitives.addGraphic(grPoly);
1458
+ padShape = "custom";
1459
+ padOptions = new PadOptions();
1460
+ padOptions.anchor = "circle";
1461
+ padSize = [0.2, 0.2];
1462
+ } else {
1463
+ padShape = "rect";
1464
+ padSize = [
1465
+ "width" in pcbPad ? pcbPad.width : 0.5,
1466
+ "height" in pcbPad ? pcbPad.height : 0.5
1467
+ ];
1468
+ }
1469
+ const pad = new FootprintPad({
1388
1470
  number: String(padNumber),
1389
1471
  padType: "smd",
1390
1472
  shape: padShape,
@@ -1397,11 +1479,18 @@ function createSmdPadFromCircuitJson({
1397
1479
  ],
1398
1480
  uuid: crypto.randomUUID()
1399
1481
  });
1482
+ if (padOptions) {
1483
+ pad.options = padOptions;
1484
+ }
1485
+ if (padPrimitives) {
1486
+ pad.primitives = padPrimitives;
1487
+ }
1488
+ return pad;
1400
1489
  }
1401
1490
 
1402
1491
  // lib/pcb/stages/utils/CreateThruHolePadFromCircuitJson.ts
1403
1492
  import { FootprintPad as FootprintPad2, PadDrill } from "kicadts";
1404
- import { applyToPoint as applyToPoint6, rotate as rotate2, identity as identity2 } from "transformation-matrix";
1493
+ import { applyToPoint as applyToPoint6, rotate as rotate2, identity } from "transformation-matrix";
1405
1494
  function createThruHolePadFromCircuitJson({
1406
1495
  platedHole,
1407
1496
  componentCenter,
@@ -1413,7 +1502,7 @@ function createThruHolePadFromCircuitJson({
1413
1502
  }
1414
1503
  const relativeX = platedHole.x - componentCenter.x;
1415
1504
  const relativeY = -(platedHole.y - componentCenter.y);
1416
- const rotationMatrix = componentRotation !== 0 ? rotate2(componentRotation * Math.PI / 180) : identity2();
1505
+ const rotationMatrix = componentRotation !== 0 ? rotate2(componentRotation * Math.PI / 180) : identity();
1417
1506
  const rotatedPos = applyToPoint6(rotationMatrix, {
1418
1507
  x: relativeX,
1419
1508
  y: relativeY
@@ -1491,7 +1580,7 @@ function createThruHolePadFromCircuitJson({
1491
1580
 
1492
1581
  // lib/pcb/stages/utils/CreateNpthPadFromCircuitJson.ts
1493
1582
  import { FootprintPad as FootprintPad3, PadDrill as PadDrill2 } from "kicadts";
1494
- import { applyToPoint as applyToPoint7, rotate as rotate3, identity as identity3 } from "transformation-matrix";
1583
+ import { applyToPoint as applyToPoint7, rotate as rotate3, identity as identity2 } from "transformation-matrix";
1495
1584
  function createNpthPadFromCircuitJson({
1496
1585
  pcbHole,
1497
1586
  componentCenter,
@@ -1502,7 +1591,7 @@ function createNpthPadFromCircuitJson({
1502
1591
  }
1503
1592
  const relativeX = pcbHole.x - componentCenter.x;
1504
1593
  const relativeY = -(pcbHole.y - componentCenter.y);
1505
- const rotationMatrix = componentRotation !== 0 ? rotate3(componentRotation * Math.PI / 180) : identity3();
1594
+ const rotationMatrix = componentRotation !== 0 ? rotate3(componentRotation * Math.PI / 180) : identity2();
1506
1595
  const rotatedPos = applyToPoint7(rotationMatrix, {
1507
1596
  x: relativeX,
1508
1597
  y: relativeY
@@ -1529,7 +1618,7 @@ function createNpthPadFromCircuitJson({
1529
1618
  });
1530
1619
  } else {
1531
1620
  padShape = "circle";
1532
- const diameter = pcbHole.hole_diameter || 1;
1621
+ const diameter = "hole_diameter" in pcbHole ? pcbHole.hole_diameter : 1;
1533
1622
  padSize = [diameter, diameter];
1534
1623
  drill = new PadDrill2({ diameter });
1535
1624
  }
@@ -1549,7 +1638,7 @@ function createNpthPadFromCircuitJson({
1549
1638
 
1550
1639
  // lib/pcb/stages/utils/CreateFpTextFromCircuitJson.ts
1551
1640
  import { FpText, TextEffects as TextEffects4, TextEffectsFont as TextEffectsFont4 } from "kicadts";
1552
- import { applyToPoint as applyToPoint8, rotate as rotate4, identity as identity4 } from "transformation-matrix";
1641
+ import { applyToPoint as applyToPoint8, rotate as rotate4, identity as identity3 } from "transformation-matrix";
1553
1642
  function createFpTextFromCircuitJson({
1554
1643
  textElement,
1555
1644
  componentCenter,
@@ -1560,7 +1649,7 @@ function createFpTextFromCircuitJson({
1560
1649
  }
1561
1650
  const relativeX = textElement.anchor_position.x - componentCenter.x;
1562
1651
  const relativeY = -(textElement.anchor_position.y - componentCenter.y);
1563
- const rotationMatrix = componentRotation !== 0 ? rotate4(componentRotation * Math.PI / 180) : identity4();
1652
+ const rotationMatrix = componentRotation !== 0 ? rotate4(componentRotation * Math.PI / 180) : identity3();
1564
1653
  const rotatedPos = applyToPoint8(rotationMatrix, {
1565
1654
  x: relativeX,
1566
1655
  y: relativeY
@@ -1945,8 +2034,8 @@ var AddGraphicsStage = class extends ConverterStage {
1945
2034
  if (board.outline && board.outline.length > 0) {
1946
2035
  corners = board.outline;
1947
2036
  } else {
1948
- const halfWidth = board.width / 2;
1949
- const halfHeight = board.height / 2;
2037
+ const halfWidth = board.width ? board.width / 2 : 0;
2038
+ const halfHeight = board.height ? board.height / 2 : 0;
1950
2039
  corners = [
1951
2040
  { x: board.center.x - halfWidth, y: board.center.y - halfHeight },
1952
2041
  { x: board.center.x + halfWidth, y: board.center.y - halfHeight },
@@ -1999,9 +2088,9 @@ var CircuitJsonToKicadPcbConverter = class {
1999
2088
  generator: "circuit-json-to-kicad",
2000
2089
  generatorVersion: "0.0.1"
2001
2090
  }),
2002
- c2kMatPcb: compose2(
2003
- translate2(KICAD_PCB_CENTER_X, KICAD_PCB_CENTER_Y),
2004
- scale2(CIRCUIT_JSON_TO_MM_SCALE, -CIRCUIT_JSON_TO_MM_SCALE)
2091
+ c2kMatPcb: compose3(
2092
+ translate3(KICAD_PCB_CENTER_X, KICAD_PCB_CENTER_Y),
2093
+ scale3(CIRCUIT_JSON_TO_MM_SCALE, -CIRCUIT_JSON_TO_MM_SCALE)
2005
2094
  )
2006
2095
  };
2007
2096
  this.pipeline = [
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.22",
4
+ "version": "0.0.24",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"
@@ -18,13 +18,13 @@
18
18
  "@tscircuit/circuit-json-util": "^0.0.72",
19
19
  "@tscircuit/common": "^0.0.5",
20
20
  "@types/bun": "latest",
21
- "circuit-json": "^0.0.267",
21
+ "circuit-json": "^0.0.302",
22
22
  "circuit-to-svg": "^0.0.208",
23
- "kicadts": "^0.0.10",
23
+ "kicadts": "^0.0.22",
24
24
  "schematic-symbols": "^0.0.202",
25
25
  "sharp": "^0.34.4",
26
26
  "transformation-matrix": "^3.1.0",
27
- "tscircuit": "^0.0.713",
27
+ "tscircuit": "^0.0.866",
28
28
  "tsup": "^8.5.0"
29
29
  },
30
30
  "peerDependencies": {