@tscircuit/cli 0.1.1480 → 0.1.1481

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/lib/index.js CHANGED
@@ -65808,7 +65808,7 @@ var getNodeHandler = (winterSpec, { port, middleware = [] }) => {
65808
65808
  }));
65809
65809
  };
65810
65810
  // package.json
65811
- var version = "0.1.1479";
65811
+ var version = "0.1.1480";
65812
65812
  var package_default = {
65813
65813
  name: "@tscircuit/cli",
65814
65814
  version,
@@ -65846,7 +65846,7 @@ var package_default = {
65846
65846
  "circuit-json": "^0.0.425",
65847
65847
  "circuit-json-to-bom-csv": "^0.0.7",
65848
65848
  "circuit-json-to-gerber": "^0.0.51",
65849
- "circuit-json-to-kicad": "0.0.137",
65849
+ "circuit-json-to-kicad": "0.0.150",
65850
65850
  "circuit-json-to-pnp-csv": "^0.0.7",
65851
65851
  "circuit-json-to-readable-netlist": "^0.0.15",
65852
65852
  "circuit-json-to-spice": "^0.0.10",
@@ -68485,12 +68485,466 @@ import {
68485
68485
  import { KicadPcb as KicadPcb2 } from "kicadts";
68486
68486
  import { cju as cju2 } from "@tscircuit/circuit-json-util";
68487
68487
  import { compose as compose6, translate as translate6, scale as scale4 } from "transformation-matrix";
68488
+
68489
+ // node_modules/earcut/src/earcut.js
68490
+ function earcut(data, holeIndices, dim = 2) {
68491
+ const hasHoles = holeIndices && holeIndices.length;
68492
+ const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
68493
+ let outerNode = linkedList(data, 0, outerLen, dim, true);
68494
+ const triangles = [];
68495
+ if (!outerNode || outerNode.next === outerNode.prev)
68496
+ return triangles;
68497
+ let minX, minY, invSize;
68498
+ if (hasHoles)
68499
+ outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
68500
+ if (data.length > 80 * dim) {
68501
+ minX = data[0];
68502
+ minY = data[1];
68503
+ let maxX = minX;
68504
+ let maxY = minY;
68505
+ for (let i = dim;i < outerLen; i += dim) {
68506
+ const x = data[i];
68507
+ const y = data[i + 1];
68508
+ if (x < minX)
68509
+ minX = x;
68510
+ if (y < minY)
68511
+ minY = y;
68512
+ if (x > maxX)
68513
+ maxX = x;
68514
+ if (y > maxY)
68515
+ maxY = y;
68516
+ }
68517
+ invSize = Math.max(maxX - minX, maxY - minY);
68518
+ invSize = invSize !== 0 ? 32767 / invSize : 0;
68519
+ }
68520
+ earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
68521
+ return triangles;
68522
+ }
68523
+ function linkedList(data, start, end, dim, clockwise) {
68524
+ let last;
68525
+ if (clockwise === signedArea(data, start, end, dim) > 0) {
68526
+ for (let i = start;i < end; i += dim)
68527
+ last = insertNode(i / dim | 0, data[i], data[i + 1], last);
68528
+ } else {
68529
+ for (let i = end - dim;i >= start; i -= dim)
68530
+ last = insertNode(i / dim | 0, data[i], data[i + 1], last);
68531
+ }
68532
+ if (last && equals(last, last.next)) {
68533
+ removeNode(last);
68534
+ last = last.next;
68535
+ }
68536
+ return last;
68537
+ }
68538
+ function filterPoints(start, end) {
68539
+ if (!start)
68540
+ return start;
68541
+ if (!end)
68542
+ end = start;
68543
+ let p = start, again;
68544
+ do {
68545
+ again = false;
68546
+ if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
68547
+ removeNode(p);
68548
+ p = end = p.prev;
68549
+ if (p === p.next)
68550
+ break;
68551
+ again = true;
68552
+ } else {
68553
+ p = p.next;
68554
+ }
68555
+ } while (again || p !== end);
68556
+ return end;
68557
+ }
68558
+ function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
68559
+ if (!ear)
68560
+ return;
68561
+ if (!pass && invSize)
68562
+ indexCurve(ear, minX, minY, invSize);
68563
+ let stop = ear;
68564
+ while (ear.prev !== ear.next) {
68565
+ const prev = ear.prev;
68566
+ const next = ear.next;
68567
+ if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
68568
+ triangles.push(prev.i, ear.i, next.i);
68569
+ removeNode(ear);
68570
+ ear = next.next;
68571
+ stop = next.next;
68572
+ continue;
68573
+ }
68574
+ ear = next;
68575
+ if (ear === stop) {
68576
+ if (!pass) {
68577
+ earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
68578
+ } else if (pass === 1) {
68579
+ ear = cureLocalIntersections(filterPoints(ear), triangles);
68580
+ earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
68581
+ } else if (pass === 2) {
68582
+ splitEarcut(ear, triangles, dim, minX, minY, invSize);
68583
+ }
68584
+ break;
68585
+ }
68586
+ }
68587
+ }
68588
+ function isEar(ear) {
68589
+ const a = ear.prev, b = ear, c = ear.next;
68590
+ if (area(a, b, c) >= 0)
68591
+ return false;
68592
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
68593
+ const x0 = Math.min(ax, bx, cx), y0 = Math.min(ay, by, cy), x1 = Math.max(ax, bx, cx), y1 = Math.max(ay, by, cy);
68594
+ let p = c.next;
68595
+ while (p !== a) {
68596
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0)
68597
+ return false;
68598
+ p = p.next;
68599
+ }
68600
+ return true;
68601
+ }
68602
+ function isEarHashed(ear, minX, minY, invSize) {
68603
+ const a = ear.prev, b = ear, c = ear.next;
68604
+ if (area(a, b, c) >= 0)
68605
+ return false;
68606
+ const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
68607
+ const x0 = Math.min(ax, bx, cx), y0 = Math.min(ay, by, cy), x1 = Math.max(ax, bx, cx), y1 = Math.max(ay, by, cy);
68608
+ const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
68609
+ let { prevZ: p, nextZ: n } = ear;
68610
+ while (p && p.z >= minZ && n && n.z <= maxZ) {
68611
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0)
68612
+ return false;
68613
+ p = p.prevZ;
68614
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0)
68615
+ return false;
68616
+ n = n.nextZ;
68617
+ }
68618
+ while (p && p.z >= minZ) {
68619
+ if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0)
68620
+ return false;
68621
+ p = p.prevZ;
68622
+ }
68623
+ while (n && n.z <= maxZ) {
68624
+ if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0)
68625
+ return false;
68626
+ n = n.nextZ;
68627
+ }
68628
+ return true;
68629
+ }
68630
+ function cureLocalIntersections(start, triangles) {
68631
+ let p = start;
68632
+ do {
68633
+ const a = p.prev, b = p.next.next;
68634
+ if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
68635
+ triangles.push(a.i, p.i, b.i);
68636
+ removeNode(p);
68637
+ removeNode(p.next);
68638
+ p = start = b;
68639
+ }
68640
+ p = p.next;
68641
+ } while (p !== start);
68642
+ return filterPoints(p);
68643
+ }
68644
+ function splitEarcut(start, triangles, dim, minX, minY, invSize) {
68645
+ let a = start;
68646
+ do {
68647
+ let b = a.next.next;
68648
+ while (b !== a.prev) {
68649
+ if (a.i !== b.i && isValidDiagonal(a, b)) {
68650
+ let c = splitPolygon(a, b);
68651
+ a = filterPoints(a, a.next);
68652
+ c = filterPoints(c, c.next);
68653
+ earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
68654
+ earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
68655
+ return;
68656
+ }
68657
+ b = b.next;
68658
+ }
68659
+ a = a.next;
68660
+ } while (a !== start);
68661
+ }
68662
+ function eliminateHoles(data, holeIndices, outerNode, dim) {
68663
+ const queue = [];
68664
+ for (let i = 0, len = holeIndices.length;i < len; i++) {
68665
+ const start = holeIndices[i] * dim;
68666
+ const end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
68667
+ const list = linkedList(data, start, end, dim, false);
68668
+ if (list === list.next)
68669
+ list.steiner = true;
68670
+ queue.push(getLeftmost(list));
68671
+ }
68672
+ queue.sort(compareXYSlope);
68673
+ for (let i = 0;i < queue.length; i++) {
68674
+ outerNode = eliminateHole(queue[i], outerNode);
68675
+ }
68676
+ return outerNode;
68677
+ }
68678
+ function compareXYSlope(a, b) {
68679
+ let result = a.x - b.x;
68680
+ if (result === 0) {
68681
+ result = a.y - b.y;
68682
+ if (result === 0) {
68683
+ const aSlope = (a.next.y - a.y) / (a.next.x - a.x);
68684
+ const bSlope = (b.next.y - b.y) / (b.next.x - b.x);
68685
+ result = aSlope - bSlope;
68686
+ }
68687
+ }
68688
+ return result;
68689
+ }
68690
+ function eliminateHole(hole, outerNode) {
68691
+ const bridge = findHoleBridge(hole, outerNode);
68692
+ if (!bridge) {
68693
+ return outerNode;
68694
+ }
68695
+ const bridgeReverse = splitPolygon(bridge, hole);
68696
+ filterPoints(bridgeReverse, bridgeReverse.next);
68697
+ return filterPoints(bridge, bridge.next);
68698
+ }
68699
+ function findHoleBridge(hole, outerNode) {
68700
+ let p = outerNode;
68701
+ const hx = hole.x;
68702
+ const hy = hole.y;
68703
+ let qx = -Infinity;
68704
+ let m;
68705
+ if (equals(hole, p))
68706
+ return p;
68707
+ do {
68708
+ if (equals(hole, p.next))
68709
+ return p.next;
68710
+ else if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
68711
+ const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
68712
+ if (x <= hx && x > qx) {
68713
+ qx = x;
68714
+ m = p.x < p.next.x ? p : p.next;
68715
+ if (x === hx)
68716
+ return m;
68717
+ }
68718
+ }
68719
+ p = p.next;
68720
+ } while (p !== outerNode);
68721
+ if (!m)
68722
+ return null;
68723
+ const stop = m;
68724
+ const mx = m.x;
68725
+ const my = m.y;
68726
+ let tanMin = Infinity;
68727
+ p = m;
68728
+ do {
68729
+ if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
68730
+ const tan = Math.abs(hy - p.y) / (hx - p.x);
68731
+ if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
68732
+ m = p;
68733
+ tanMin = tan;
68734
+ }
68735
+ }
68736
+ p = p.next;
68737
+ } while (p !== stop);
68738
+ return m;
68739
+ }
68740
+ function sectorContainsSector(m, p) {
68741
+ return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
68742
+ }
68743
+ function indexCurve(start, minX, minY, invSize) {
68744
+ let p = start;
68745
+ do {
68746
+ if (p.z === 0)
68747
+ p.z = zOrder(p.x, p.y, minX, minY, invSize);
68748
+ p.prevZ = p.prev;
68749
+ p.nextZ = p.next;
68750
+ p = p.next;
68751
+ } while (p !== start);
68752
+ p.prevZ.nextZ = null;
68753
+ p.prevZ = null;
68754
+ sortLinked(p);
68755
+ }
68756
+ function sortLinked(list) {
68757
+ let numMerges;
68758
+ let inSize = 1;
68759
+ do {
68760
+ let p = list;
68761
+ let e;
68762
+ list = null;
68763
+ let tail = null;
68764
+ numMerges = 0;
68765
+ while (p) {
68766
+ numMerges++;
68767
+ let q = p;
68768
+ let pSize = 0;
68769
+ for (let i = 0;i < inSize; i++) {
68770
+ pSize++;
68771
+ q = q.nextZ;
68772
+ if (!q)
68773
+ break;
68774
+ }
68775
+ let qSize = inSize;
68776
+ while (pSize > 0 || qSize > 0 && q) {
68777
+ if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
68778
+ e = p;
68779
+ p = p.nextZ;
68780
+ pSize--;
68781
+ } else {
68782
+ e = q;
68783
+ q = q.nextZ;
68784
+ qSize--;
68785
+ }
68786
+ if (tail)
68787
+ tail.nextZ = e;
68788
+ else
68789
+ list = e;
68790
+ e.prevZ = tail;
68791
+ tail = e;
68792
+ }
68793
+ p = q;
68794
+ }
68795
+ tail.nextZ = null;
68796
+ inSize *= 2;
68797
+ } while (numMerges > 1);
68798
+ return list;
68799
+ }
68800
+ function zOrder(x, y, minX, minY, invSize) {
68801
+ x = (x - minX) * invSize | 0;
68802
+ y = (y - minY) * invSize | 0;
68803
+ x = (x | x << 8) & 16711935;
68804
+ x = (x | x << 4) & 252645135;
68805
+ x = (x | x << 2) & 858993459;
68806
+ x = (x | x << 1) & 1431655765;
68807
+ y = (y | y << 8) & 16711935;
68808
+ y = (y | y << 4) & 252645135;
68809
+ y = (y | y << 2) & 858993459;
68810
+ y = (y | y << 1) & 1431655765;
68811
+ return x | y << 1;
68812
+ }
68813
+ function getLeftmost(start) {
68814
+ let p = start, leftmost = start;
68815
+ do {
68816
+ if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y)
68817
+ leftmost = p;
68818
+ p = p.next;
68819
+ } while (p !== start);
68820
+ return leftmost;
68821
+ }
68822
+ function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
68823
+ return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py);
68824
+ }
68825
+ function pointInTriangleExceptFirst(ax, ay, bx, by, cx, cy, px, py) {
68826
+ return !(ax === px && ay === py) && pointInTriangle(ax, ay, bx, by, cx, cy, px, py);
68827
+ }
68828
+ function isValidDiagonal(a, b) {
68829
+ return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && (area(a.prev, a, b.prev) || area(a, b.prev, b)) || equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
68830
+ }
68831
+ function area(p, q, r) {
68832
+ return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
68833
+ }
68834
+ function equals(p1, p2) {
68835
+ return p1.x === p2.x && p1.y === p2.y;
68836
+ }
68837
+ function intersects(p1, q1, p2, q2) {
68838
+ const o1 = sign(area(p1, q1, p2));
68839
+ const o2 = sign(area(p1, q1, q2));
68840
+ const o3 = sign(area(p2, q2, p1));
68841
+ const o4 = sign(area(p2, q2, q1));
68842
+ if (o1 !== o2 && o3 !== o4)
68843
+ return true;
68844
+ if (o1 === 0 && onSegment(p1, p2, q1))
68845
+ return true;
68846
+ if (o2 === 0 && onSegment(p1, q2, q1))
68847
+ return true;
68848
+ if (o3 === 0 && onSegment(p2, p1, q2))
68849
+ return true;
68850
+ if (o4 === 0 && onSegment(p2, q1, q2))
68851
+ return true;
68852
+ return false;
68853
+ }
68854
+ function onSegment(p, q, r) {
68855
+ return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
68856
+ }
68857
+ function sign(num) {
68858
+ return num > 0 ? 1 : num < 0 ? -1 : 0;
68859
+ }
68860
+ function intersectsPolygon(a, b) {
68861
+ let p = a;
68862
+ do {
68863
+ if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b))
68864
+ return true;
68865
+ p = p.next;
68866
+ } while (p !== a);
68867
+ return false;
68868
+ }
68869
+ function locallyInside(a, b) {
68870
+ return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
68871
+ }
68872
+ function middleInside(a, b) {
68873
+ let p = a;
68874
+ let inside = false;
68875
+ const px = (a.x + b.x) / 2;
68876
+ const py = (a.y + b.y) / 2;
68877
+ do {
68878
+ if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)
68879
+ inside = !inside;
68880
+ p = p.next;
68881
+ } while (p !== a);
68882
+ return inside;
68883
+ }
68884
+ function splitPolygon(a, b) {
68885
+ const a2 = createNode(a.i, a.x, a.y), b2 = createNode(b.i, b.x, b.y), an = a.next, bp = b.prev;
68886
+ a.next = b;
68887
+ b.prev = a;
68888
+ a2.next = an;
68889
+ an.prev = a2;
68890
+ b2.next = a2;
68891
+ a2.prev = b2;
68892
+ bp.next = b2;
68893
+ b2.prev = bp;
68894
+ return b2;
68895
+ }
68896
+ function insertNode(i, x, y, last) {
68897
+ const p = createNode(i, x, y);
68898
+ if (!last) {
68899
+ p.prev = p;
68900
+ p.next = p;
68901
+ } else {
68902
+ p.next = last.next;
68903
+ p.prev = last;
68904
+ last.next.prev = p;
68905
+ last.next = p;
68906
+ }
68907
+ return p;
68908
+ }
68909
+ function removeNode(p) {
68910
+ p.next.prev = p.prev;
68911
+ p.prev.next = p.next;
68912
+ if (p.prevZ)
68913
+ p.prevZ.nextZ = p.nextZ;
68914
+ if (p.nextZ)
68915
+ p.nextZ.prevZ = p.prevZ;
68916
+ }
68917
+ function createNode(i, x, y) {
68918
+ return {
68919
+ i,
68920
+ x,
68921
+ y,
68922
+ prev: null,
68923
+ next: null,
68924
+ z: 0,
68925
+ prevZ: null,
68926
+ nextZ: null,
68927
+ steiner: false
68928
+ };
68929
+ }
68930
+ function signedArea(data, start, end, dim) {
68931
+ let sum = 0;
68932
+ for (let i = start, j = end - dim;i < end; i += dim) {
68933
+ sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
68934
+ j = i;
68935
+ }
68936
+ return sum;
68937
+ }
68938
+
68939
+ // node_modules/circuit-json-to-kicad/dist/index.js
68488
68940
  import {
68941
+ Layer,
68489
68942
  Pts as Pts3,
68490
68943
  Xy as Xy3,
68491
68944
  Zone,
68492
68945
  ZoneConnectPads,
68493
68946
  ZoneFill,
68947
+ ZoneFilledPolygon,
68494
68948
  ZoneHatch,
68495
68949
  ZonePolygon
68496
68950
  } from "kicadts";
@@ -68576,7 +69030,7 @@ import { Via, ViaNet } from "kicadts";
68576
69030
  import { applyToPoint as applyToPoint18 } from "transformation-matrix";
68577
69031
  import { Footprint as Footprint4 } from "kicadts";
68578
69032
  import { applyToPoint as applyToPoint19 } from "transformation-matrix";
68579
- import { GrLine } from "kicadts";
69033
+ import { GrCircle, GrLine, GrPoly } from "kicadts";
68580
69034
  import {
68581
69035
  At as At2,
68582
69036
  GrText,
@@ -68585,7 +69039,7 @@ import {
68585
69039
  TextEffectsJustify as TextEffectsJustify3
68586
69040
  } from "kicadts";
68587
69041
  import { applyToPoint as applyToPoint20 } from "transformation-matrix";
68588
- import { applyToPoint as applyToPoint22 } from "transformation-matrix";
69042
+ import { applyToPoint as applyToPoint22, rotate as rotate9 } from "transformation-matrix";
68589
69043
  import {
68590
69044
  GrText as GrText2,
68591
69045
  TextEffects as TextEffects11,
@@ -68730,8 +69184,9 @@ function getReferencePrefixForComponent(sourceComponent) {
68730
69184
  }
68731
69185
  function getReferenceDesignator(sourceComponent) {
68732
69186
  const name = sourceComponent?.name;
68733
- if (isReferenceDesignator(name)) {
68734
- return name.trim();
69187
+ const trimmedName = name?.trim();
69188
+ if (trimmedName) {
69189
+ return trimmedName;
68735
69190
  }
68736
69191
  const prefix = getReferencePrefixForComponent(sourceComponent);
68737
69192
  return `${prefix}?`;
@@ -70329,6 +70784,8 @@ function generateDeterministicUuid(data) {
70329
70784
  const hash = simpleHash(data);
70330
70785
  return `${hash.slice(0, 8)}-${hash.slice(8, 12)}-${hash.slice(12, 16)}-${hash.slice(16, 20)}-${hash.slice(20, 32)}`;
70331
70786
  }
70787
+ var isPcbCopperPour = (element) => element.type === "pcb_copper_pour";
70788
+ var getCopperPours = (circuitJson) => circuitJson.filter(isPcbCopperPour);
70332
70789
  var getCopperPourNetInfo = (pour, ctx) => {
70333
70790
  let connectivityKey = pour.source_net_id;
70334
70791
  if (connectivityKey) {
@@ -70339,19 +70796,13 @@ var getCopperPourNetInfo = (pour, ctx) => {
70339
70796
  return;
70340
70797
  return ctx.pcbNetMap?.get(connectivityKey);
70341
70798
  };
70342
- var getRingPoints = (ring, c2kMatPcb) => {
70343
- return (ring.vertices ?? []).map((point) => {
70344
- const transformedPoint = applyToPoint8(c2kMatPcb, point);
70345
- return new Xy3(transformedPoint.x, transformedPoint.y);
70346
- });
70347
- };
70348
- var getPolygonPoints = (points, c2kMatPcb) => {
70799
+ var convertPointsToKicadXy = (points, c2kMatPcb) => {
70349
70800
  return (points ?? []).map((point) => {
70350
70801
  const transformedPoint = applyToPoint8(c2kMatPcb, point);
70351
70802
  return new Xy3(transformedPoint.x, transformedPoint.y);
70352
70803
  });
70353
70804
  };
70354
- var rotatePointsToTopRight = (points) => {
70805
+ var rotateRingToStartAtTopRight = (points) => {
70355
70806
  if (points.length < 2)
70356
70807
  return points;
70357
70808
  let startIndex = 0;
@@ -70366,6 +70817,26 @@ var rotatePointsToTopRight = (points) => {
70366
70817
  }
70367
70818
  return [...points.slice(startIndex), ...points.slice(0, startIndex)];
70368
70819
  };
70820
+ var removeConsecutiveDuplicatePoints = (points) => {
70821
+ const dedupedPoints = [];
70822
+ for (const point of points) {
70823
+ const lastPoint = dedupedPoints[dedupedPoints.length - 1];
70824
+ if (lastPoint?.x === point.x && lastPoint.y === point.y) {
70825
+ continue;
70826
+ }
70827
+ dedupedPoints.push(point);
70828
+ }
70829
+ return dedupedPoints;
70830
+ };
70831
+ var removeRepeatedClosingPoint = (points) => {
70832
+ const dedupedPoints = [...points];
70833
+ const firstPoint = dedupedPoints[0];
70834
+ const lastPoint = dedupedPoints[dedupedPoints.length - 1];
70835
+ if (firstPoint && lastPoint && dedupedPoints.length > 1 && firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) {
70836
+ dedupedPoints.pop();
70837
+ }
70838
+ return dedupedPoints;
70839
+ };
70369
70840
  var getRectRingPoints = (pour, c2kMatPcb) => {
70370
70841
  const ccwRotationDegrees = pour.rotation ?? 0;
70371
70842
  const cornerTransform = compose3(translate3(pour.center.x, pour.center.y), rotate(ccwRotationDegrees * Math.PI / 180));
@@ -70377,17 +70848,87 @@ var getRectRingPoints = (pour, c2kMatPcb) => {
70377
70848
  { x: halfWidth, y: halfHeight },
70378
70849
  { x: -halfWidth, y: halfHeight }
70379
70850
  ].map((corner) => applyToPoint8(cornerTransform, corner));
70380
- return rotatePointsToTopRight(getPolygonPoints(corners, c2kMatPcb));
70851
+ return convertPointsToKicadXy(corners, c2kMatPcb);
70852
+ };
70853
+ var getPolygonRingPoints = (pour, c2kMatPcb) => removeRepeatedClosingPoint(removeConsecutiveDuplicatePoints(rotateRingToStartAtTopRight(convertPointsToKicadXy(pour.points, c2kMatPcb))));
70854
+ var getBrepZoneRings = (pour, c2kMatPcb) => [
70855
+ removeRepeatedClosingPoint(removeConsecutiveDuplicatePoints(rotateRingToStartAtTopRight(convertPointsToKicadXy(pour.brep_shape.outer_ring.vertices, c2kMatPcb)))),
70856
+ pour.brep_shape.inner_rings.map((ring) => removeRepeatedClosingPoint(removeConsecutiveDuplicatePoints(rotateRingToStartAtTopRight(convertPointsToKicadXy(ring.vertices, c2kMatPcb))))).filter((ringPoints) => ringPoints.length >= 3)
70857
+ ];
70858
+ var getCopperPourZoneRings = (pour, c2kMatPcb) => {
70859
+ switch (pour.shape) {
70860
+ case "rect":
70861
+ return [
70862
+ removeRepeatedClosingPoint(removeConsecutiveDuplicatePoints(rotateRingToStartAtTopRight(getRectRingPoints(pour, c2kMatPcb)))),
70863
+ []
70864
+ ];
70865
+ case "polygon":
70866
+ return [getPolygonRingPoints(pour, c2kMatPcb), []];
70867
+ case "brep":
70868
+ return getBrepZoneRings(pour, c2kMatPcb);
70869
+ }
70381
70870
  };
70382
- var getCopperPourPolygonPoints = (pour, c2kMatPcb) => {
70383
- if (pour.shape === "rect") {
70384
- return getRectRingPoints(pour, c2kMatPcb);
70871
+ var createZonePolygons = (outerRing, innerRings) => {
70872
+ const polygons = [];
70873
+ if (outerRing.length >= 3) {
70874
+ polygons.push(new ZonePolygon(new Pts3(outerRing)));
70385
70875
  }
70386
- if (pour.shape === "polygon") {
70387
- return rotatePointsToTopRight(getPolygonPoints(pour.points, c2kMatPcb));
70876
+ for (const innerRing of innerRings) {
70877
+ if (innerRing.length < 3)
70878
+ continue;
70879
+ polygons.push(new ZonePolygon(new Pts3(innerRing)));
70388
70880
  }
70389
- const outerRing = pour.brep_shape?.outer_ring;
70390
- return outerRing ? rotatePointsToTopRight(getRingPoints(outerRing, c2kMatPcb)) : [];
70881
+ return polygons;
70882
+ };
70883
+ var getTriangleArea = (a, b, c) => Math.abs((a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y)) / 2);
70884
+ var createZoneFilledPolygons = (outerRing, innerRings, kicadLayer) => {
70885
+ if (outerRing.length < 3) {
70886
+ return [];
70887
+ }
70888
+ const layer = new Layer([kicadLayer]);
70889
+ if (innerRings.length === 0) {
70890
+ return [
70891
+ new ZoneFilledPolygon({
70892
+ layer,
70893
+ pts: new Pts3(outerRing)
70894
+ })
70895
+ ];
70896
+ }
70897
+ const flattenedPoints = [];
70898
+ const holeIndices = [];
70899
+ let pointIndex = 0;
70900
+ const addRing = (ring) => {
70901
+ for (const point of ring) {
70902
+ flattenedPoints.push(point.x, point.y);
70903
+ }
70904
+ pointIndex += ring.length;
70905
+ };
70906
+ addRing(outerRing);
70907
+ for (const innerRing of innerRings) {
70908
+ holeIndices.push(pointIndex);
70909
+ addRing(innerRing);
70910
+ }
70911
+ const triangleIndices = earcut(flattenedPoints, holeIndices, 2);
70912
+ const filledPolygons = [];
70913
+ for (let i = 0;i < triangleIndices.length; i += 3) {
70914
+ const trianglePoints = [];
70915
+ for (let offset = 0;offset < 3; offset++) {
70916
+ const pointOffset = triangleIndices[i + offset] * 2;
70917
+ const x = flattenedPoints[pointOffset];
70918
+ const y = flattenedPoints[pointOffset + 1];
70919
+ if (x === undefined || y === undefined)
70920
+ continue;
70921
+ trianglePoints.push(new Xy3(x, y));
70922
+ }
70923
+ if (trianglePoints.length !== 3 || getTriangleArea(trianglePoints[0], trianglePoints[1], trianglePoints[2]) === 0) {
70924
+ continue;
70925
+ }
70926
+ filledPolygons.push(new ZoneFilledPolygon({
70927
+ layer,
70928
+ pts: new Pts3(trianglePoints)
70929
+ }));
70930
+ }
70931
+ return filledPolygons;
70391
70932
  };
70392
70933
  var AddCopperPoursStage = class extends ConverterStage {
70393
70934
  _step() {
@@ -70398,19 +70939,18 @@ var AddCopperPoursStage = class extends ConverterStage {
70398
70939
  if (!c2kMatPcb) {
70399
70940
  throw new Error("PCB transformation matrix not initialized in context");
70400
70941
  }
70401
- const copperPours = this.ctx.db.pcb_copper_pour?.list();
70402
- for (const pour of copperPours ?? []) {
70403
- const polygonPoints = getCopperPourPolygonPoints(pour, c2kMatPcb);
70404
- if (polygonPoints.length < 3)
70942
+ const copperPours = getCopperPours(this.input);
70943
+ for (const pour of copperPours) {
70944
+ const [outerRing, innerRings] = getCopperPourZoneRings(pour, c2kMatPcb);
70945
+ if (outerRing.length < 3)
70405
70946
  continue;
70406
70947
  const netInfo = getCopperPourNetInfo(pour, this.ctx);
70407
70948
  const kicadLayer = getKicadLayer(pour.layer);
70408
- const polygonPts = new Pts3(polygonPoints);
70409
70949
  const zone = new Zone({
70410
70950
  net: netInfo?.id ?? 0,
70411
70951
  netName: netInfo?.name ?? "",
70412
70952
  layer: kicadLayer,
70413
- uuid: generateDeterministicUuid(`zone:${pour.pcb_copper_pour_id ?? ""}`),
70953
+ uuid: generateDeterministicUuid(`zone:${pour.pcb_copper_pour_id}`),
70414
70954
  hatch: new ZoneHatch("edge", 0.5),
70415
70955
  connectPads: new ZoneConnectPads({ enabled: true, clearance: 0.15 }),
70416
70956
  minThickness: 0.25,
@@ -70418,9 +70958,11 @@ var AddCopperPoursStage = class extends ConverterStage {
70418
70958
  fill: new ZoneFill({
70419
70959
  filled: true,
70420
70960
  thermalGap: 0.5,
70421
- thermalBridgeWidth: 0.5
70961
+ thermalBridgeWidth: 0.5,
70962
+ islandRemovalMode: 0
70422
70963
  }),
70423
- polygons: [new ZonePolygon(polygonPts)]
70964
+ polygons: createZonePolygons(outerRing, innerRings),
70965
+ filledPolygons: createZoneFilledPolygons(outerRing, innerRings, kicadLayer)
70424
70966
  });
70425
70967
  const zones = kicadPcb.zones;
70426
70968
  zones.push(zone);
@@ -71090,7 +71632,8 @@ function convertCourtyardRects(courtyardRects, componentCenter) {
71090
71632
  }
71091
71633
  return fpRects;
71092
71634
  }
71093
- function convertCourtyardOutlines(courtyardOutlines, componentCenter, componentRotation = 0) {
71635
+ function convertCourtyardOutlines(params2) {
71636
+ const { courtyardOutlines, componentCenter, componentRotation = 0 } = params2;
71094
71637
  const fpPolys = [];
71095
71638
  const cj2kicadMatrix = compose4(componentRotation !== 0 ? rotate2(componentRotation * Math.PI / 180) : { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, scale2(1, -1), translate4(-componentCenter.x, -componentCenter.y));
71096
71639
  for (const outline of courtyardOutlines) {
@@ -71161,7 +71704,13 @@ function createFpTextFromCircuitJson({
71161
71704
  effects: textEffects
71162
71705
  });
71163
71706
  }
71164
- function convertSilkscreenTexts(silkscreenTexts, componentCenter, componentRotation, sourceComponentName) {
71707
+ function convertSilkscreenTexts(params2) {
71708
+ const {
71709
+ silkscreenTexts,
71710
+ componentCenter,
71711
+ componentRotation,
71712
+ sourceComponentName
71713
+ } = params2;
71165
71714
  const fpTexts = [];
71166
71715
  for (const textElement of silkscreenTexts) {
71167
71716
  const fpText = createFpTextFromCircuitJson({
@@ -71217,7 +71766,8 @@ function convertSilkscreenPaths(silkscreenPaths, { componentCenter, componentRot
71217
71766
  }
71218
71767
  return fpLines;
71219
71768
  }
71220
- function convertNoteTexts(noteTexts, componentCenter, componentRotation) {
71769
+ function convertNoteTexts(params2) {
71770
+ const { noteTexts, componentCenter, componentRotation } = params2;
71221
71771
  const fpTexts = [];
71222
71772
  for (const textElement of noteTexts) {
71223
71773
  const relX = textElement.anchor_position.x - componentCenter.x;
@@ -71247,17 +71797,19 @@ function create3DModelsFromCadComponent(cadComponent, componentCenter, options)
71247
71797
  const model = new FootprintModel3(modelUrl);
71248
71798
  if (cadComponent.position) {
71249
71799
  const boardLayerZOffset = options?.boardLayerZOffset ?? 0;
71800
+ const modelOriginPosition = cadComponent.model_origin_position;
71250
71801
  model.offset = {
71251
- x: (cadComponent.position.x || 0) - componentCenter.x,
71252
- y: (cadComponent.position.y || 0) - componentCenter.y,
71253
- z: (cadComponent.position.z || 0) - boardLayerZOffset
71802
+ x: (cadComponent.position.x || 0) - componentCenter.x - (modelOriginPosition?.x || 0),
71803
+ y: (cadComponent.position.y || 0) - componentCenter.y - (modelOriginPosition?.y || 0),
71804
+ z: (cadComponent.position.z || 0) - boardLayerZOffset - (modelOriginPosition?.z || 0)
71254
71805
  };
71255
71806
  }
71256
71807
  if (cadComponent.rotation) {
71808
+ const footprintRotation = options?.footprintRotation ?? 0;
71257
71809
  model.rotate = {
71258
71810
  x: cadComponent.rotation.x || 0,
71259
71811
  y: cadComponent.rotation.y || 0,
71260
- z: cadComponent.rotation.z || 0
71812
+ z: (cadComponent.rotation.z || 0) - footprintRotation
71261
71813
  };
71262
71814
  }
71263
71815
  if (cadComponent.model_unit_to_mm_scale_factor) {
@@ -71336,6 +71888,15 @@ function createSmdPadFromCircuitJson({
71336
71888
  }
71337
71889
  padSize = [pcbPad.width, pcbPad.height];
71338
71890
  rotation = pcbPad.ccw_rotation;
71891
+ } else if (pcbPad.shape === "pill") {
71892
+ padShape = "roundrect";
71893
+ padSize = [pcbPad.width, pcbPad.height];
71894
+ roundrect_rratio = pcbPad.radius / Math.min(pcbPad.width, pcbPad.height);
71895
+ } else if (pcbPad.shape === "rotated_pill") {
71896
+ padShape = "roundrect";
71897
+ padSize = [pcbPad.width, pcbPad.height];
71898
+ roundrect_rratio = pcbPad.radius / Math.min(pcbPad.width, pcbPad.height);
71899
+ rotation = pcbPad.ccw_rotation;
71339
71900
  } else if (pcbPad.shape === "rect") {
71340
71901
  const cornerRadius = pcbPad.corner_radius ?? pcbPad.rect_border_radius;
71341
71902
  if (cornerRadius) {
@@ -71359,6 +71920,7 @@ function createSmdPadFromCircuitJson({
71359
71920
  `${padLayer === "F.Cu" ? "F" : "B"}.Mask`
71360
71921
  ],
71361
71922
  solderMaskMargin: pcbPad.soldermask_margin,
71923
+ roundrectRatio: roundrect_rratio,
71362
71924
  uuid: generateDeterministicUuid(padData)
71363
71925
  });
71364
71926
  if (padOptions) {
@@ -71602,7 +72164,8 @@ function createNpthPadFromCircuitJson({
71602
72164
  uuid: crypto.randomUUID()
71603
72165
  });
71604
72166
  }
71605
- function convertNpthHoles(pcbHoles, componentCenter, componentRotation) {
72167
+ function convertNpthHoles(params2) {
72168
+ const { pcbHoles, componentCenter, componentRotation } = params2;
71606
72169
  const pads = [];
71607
72170
  for (const pcbHole of pcbHoles) {
71608
72171
  const pad = createNpthPadFromCircuitJson({
@@ -71675,9 +72238,18 @@ var AddFootprintsStage = class extends ConverterStage {
71675
72238
  });
71676
72239
  const fpTexts = footprint.fpTexts;
71677
72240
  const pcbSilkscreenTexts = this.ctx.db.pcb_silkscreen_text?.list().filter((text) => text.pcb_component_id === component.pcb_component_id) || [];
71678
- fpTexts.push(...convertSilkscreenTexts(pcbSilkscreenTexts, component.center, component.rotation || 0, sourceComponent?.name));
72241
+ fpTexts.push(...convertSilkscreenTexts({
72242
+ silkscreenTexts: pcbSilkscreenTexts,
72243
+ componentCenter: component.center,
72244
+ componentRotation: component.rotation || 0,
72245
+ sourceComponentName: sourceComponent?.name
72246
+ }));
71679
72247
  const pcbNoteTexts = this.ctx.db.pcb_note_text?.list().filter((text) => text.pcb_component_id === component.pcb_component_id) || [];
71680
- fpTexts.push(...convertNoteTexts(pcbNoteTexts, component.center, component.rotation || 0));
72248
+ fpTexts.push(...convertNoteTexts({
72249
+ noteTexts: pcbNoteTexts,
72250
+ componentCenter: component.center,
72251
+ componentRotation: component.rotation || 0
72252
+ }));
71681
72253
  footprint.fpTexts = fpTexts;
71682
72254
  const pcbSilkscreenPaths = this.ctx.db.pcb_silkscreen_path?.list().filter((path7) => path7.pcb_component_id === component.pcb_component_id) || [];
71683
72255
  const fpLines = footprint.fpLines ?? [];
@@ -71709,7 +72281,11 @@ var AddFootprintsStage = class extends ConverterStage {
71709
72281
  }, this.ctx);
71710
72282
  fpPads.push(...thruHolePads);
71711
72283
  const pcbHoles = this.ctx.db.pcb_hole?.list().filter((hole) => hole.pcb_component_id === component.pcb_component_id) || [];
71712
- const npthPads = convertNpthHoles(pcbHoles, component.center, component.rotation || 0);
72284
+ const npthPads = convertNpthHoles({
72285
+ pcbHoles,
72286
+ componentCenter: component.center,
72287
+ componentRotation: component.rotation || 0
72288
+ });
71713
72289
  fpPads.push(...npthPads);
71714
72290
  footprint.fpPads = fpPads;
71715
72291
  const pcbSilkscreenCircles = this.ctx.db.pcb_silkscreen_circle?.list().filter((circle) => circle.pcb_component_id === component.pcb_component_id) || [];
@@ -71727,7 +72303,11 @@ var AddFootprintsStage = class extends ConverterStage {
71727
72303
  fpRects.push(...convertCourtyardRects(pcbCourtyardRects, component.center));
71728
72304
  footprint.fpRects = fpRects;
71729
72305
  const pcbCourtyardOutlines = this.ctx.db.pcb_courtyard_outline?.list().filter((outline) => outline.pcb_component_id === component.pcb_component_id) || [];
71730
- const fpPolys = convertCourtyardOutlines(pcbCourtyardOutlines, component.center, component.rotation || 0);
72306
+ const fpPolys = convertCourtyardOutlines({
72307
+ courtyardOutlines: pcbCourtyardOutlines,
72308
+ componentCenter: component.center,
72309
+ componentRotation: component.rotation || 0
72310
+ });
71731
72311
  if (fpPolys.length > 0) {
71732
72312
  footprint.fpPolys = fpPolys;
71733
72313
  }
@@ -71735,7 +72315,10 @@ var AddFootprintsStage = class extends ConverterStage {
71735
72315
  const pcbBoard = this.ctx.db.pcb_board?.list()[0];
71736
72316
  const boardThickness = pcbBoard?.thickness ?? 0;
71737
72317
  const boardLayerZOffset = component.layer === "bottom" ? -(boardThickness / 2) : boardThickness / 2;
71738
- const models = create3DModelsFromCadComponent(cadComponent, component.center, { boardLayerZOffset });
72318
+ const models = create3DModelsFromCadComponent(cadComponent, component.center, {
72319
+ boardLayerZOffset,
72320
+ footprintRotation: component.rotation || 0
72321
+ });
71739
72322
  const KICAD_3D_BASE = "${KIPRJMOD}/3dmodels";
71740
72323
  if (models.length > 0) {
71741
72324
  if (this.includeBuiltin3dModels) {
@@ -71789,6 +72372,24 @@ var AddFootprintsStage = class extends ConverterStage {
71789
72372
  return this.ctx.kicadPcb;
71790
72373
  }
71791
72374
  };
72375
+ function getRoutePointLayer(point, pointRoleInSegment) {
72376
+ if ("layer" in point) {
72377
+ return point.layer;
72378
+ }
72379
+ if (point.route_type === "through_pad") {
72380
+ if (pointRoleInSegment === "start") {
72381
+ return point.end_layer;
72382
+ }
72383
+ return point.start_layer;
72384
+ }
72385
+ return;
72386
+ }
72387
+ function getRoutePointWidth(point) {
72388
+ if ("width" in point) {
72389
+ return point.width;
72390
+ }
72391
+ return;
72392
+ }
71792
72393
  var AddTracesStage = class extends ConverterStage {
71793
72394
  tracesProcessed = 0;
71794
72395
  pcbTraces = [];
@@ -71796,6 +72397,18 @@ var AddTracesStage = class extends ConverterStage {
71796
72397
  super(input, ctx);
71797
72398
  this.pcbTraces = this.ctx.db.pcb_trace.list();
71798
72399
  }
72400
+ getRoutePointPosition(point, pointRoleInSegment) {
72401
+ if ("x" in point && "y" in point) {
72402
+ return { x: point.x, y: point.y };
72403
+ }
72404
+ if (point.route_type !== "through_pad") {
72405
+ return null;
72406
+ }
72407
+ if (pointRoleInSegment === "start") {
72408
+ return { x: point.end.x, y: point.end.y };
72409
+ }
72410
+ return { x: point.start.x, y: point.start.y };
72411
+ }
71799
72412
  _step() {
71800
72413
  const { kicadPcb, c2kMatPcb, pcbNetMap } = this.ctx;
71801
72414
  if (!kicadPcb) {
@@ -71809,22 +72422,25 @@ var AddTracesStage = class extends ConverterStage {
71809
72422
  return;
71810
72423
  }
71811
72424
  const trace = this.pcbTraces[this.tracesProcessed];
71812
- if (!trace.route || trace.route.length < 2) {
72425
+ const route = trace.route;
72426
+ if (!route || route.length < 2) {
71813
72427
  this.tracesProcessed++;
71814
72428
  return;
71815
72429
  }
71816
- let lastKnownLayer = trace.route[0]?.layer;
71817
- for (let i = 0;i < trace.route.length - 1; i++) {
71818
- const startPoint = trace.route[i];
71819
- const endPoint = trace.route[i + 1];
71820
- const transformedStart = applyToPoint17(c2kMatPcb, {
71821
- x: startPoint.x,
71822
- y: startPoint.y
71823
- });
71824
- const transformedEnd = applyToPoint17(c2kMatPcb, {
71825
- x: endPoint.x,
71826
- y: endPoint.y
71827
- });
72430
+ let lastKnownLayer = getRoutePointLayer(route[0], "start");
72431
+ for (let i = 0;i < route.length - 1; i++) {
72432
+ const startPoint = route[i];
72433
+ const endPoint = route[i + 1];
72434
+ const startPosition = this.getRoutePointPosition(startPoint, "start");
72435
+ const endPosition = this.getRoutePointPosition(endPoint, "end");
72436
+ if (!startPosition || !endPosition) {
72437
+ throw new Error(`Unable to convert pcb_trace route segment ${trace.pcb_trace_id ?? this.tracesProcessed}:${i} to KiCad segment`);
72438
+ }
72439
+ const transformedStart = applyToPoint17(c2kMatPcb, startPosition);
72440
+ const transformedEnd = applyToPoint17(c2kMatPcb, endPosition);
72441
+ if (transformedStart.x === transformedEnd.x && transformedStart.y === transformedEnd.y) {
72442
+ continue;
72443
+ }
71828
72444
  let netInfo;
71829
72445
  if (pcbNetMap) {
71830
72446
  let connectivityKey = trace.subcircuit_connectivity_map_key;
@@ -71853,25 +72469,27 @@ var AddTracesStage = class extends ConverterStage {
71853
72469
  netInfo = pcbNetMap.get(connectivityKey);
71854
72470
  }
71855
72471
  }
71856
- const segmentLayerSource = startPoint.layer ?? endPoint.layer ?? lastKnownLayer;
72472
+ const segmentLayerSource = getRoutePointLayer(startPoint, "start") ?? getRoutePointLayer(endPoint, "end") ?? lastKnownLayer;
71857
72473
  const kicadLayer = getKicadLayer(segmentLayerSource);
71858
72474
  const segmentData = `segment:${transformedStart.x},${transformedStart.y}:${transformedEnd.x},${transformedEnd.y}:${kicadLayer}:${netInfo?.id ?? 0}`;
71859
72475
  const segment = new Segment({
71860
72476
  start: { x: transformedStart.x, y: transformedStart.y },
71861
72477
  end: { x: transformedEnd.x, y: transformedEnd.y },
71862
72478
  layer: kicadLayer,
71863
- width: startPoint.width ?? endPoint.width ?? trace.width ?? 0.25,
72479
+ width: getRoutePointWidth(startPoint) ?? getRoutePointWidth(endPoint) ?? trace.width ?? 0.25,
71864
72480
  net: new SegmentNet(netInfo?.id ?? 0),
71865
72481
  uuid: generateDeterministicUuid(segmentData)
71866
72482
  });
71867
72483
  const segments = kicadPcb.segments;
71868
72484
  segments.push(segment);
71869
72485
  kicadPcb.segments = segments;
71870
- if (startPoint.layer) {
71871
- lastKnownLayer = startPoint.layer;
72486
+ const startLayer = getRoutePointLayer(startPoint, "start");
72487
+ const endLayer = getRoutePointLayer(endPoint, "end");
72488
+ if (startLayer) {
72489
+ lastKnownLayer = startLayer;
71872
72490
  }
71873
- if (endPoint.layer) {
71874
- lastKnownLayer = endPoint.layer;
72491
+ if (endLayer) {
72492
+ lastKnownLayer = endLayer;
71875
72493
  }
71876
72494
  }
71877
72495
  this.tracesProcessed++;
@@ -72071,7 +72689,11 @@ var AddStandalonePcbElements = class extends ConverterStage {
72071
72689
  uuid: generateDeterministicUuid(footprintSeed)
72072
72690
  });
72073
72691
  const ccwRotationDegrees = 0;
72074
- const npthPads = convertNpthHoles([hole], { x: hole.x, y: hole.y }, ccwRotationDegrees);
72692
+ const npthPads = convertNpthHoles({
72693
+ pcbHoles: [hole],
72694
+ componentCenter: { x: hole.x, y: hole.y },
72695
+ componentRotation: ccwRotationDegrees
72696
+ });
72075
72697
  if (npthPads.length > 0) {
72076
72698
  footprint.fpPads = npthPads;
72077
72699
  const footprints = kicadPcb.footprints;
@@ -72222,7 +72844,8 @@ function createFabricationNoteTextFromCircuitJson({
72222
72844
  const grText = new GrText({
72223
72845
  text: textElement.text,
72224
72846
  layer: kicadLayer,
72225
- effects: textEffects
72847
+ effects: textEffects,
72848
+ uuid: generateDeterministicUuid(textElement.pcb_fabrication_note_text_id ?? textElement.text)
72226
72849
  });
72227
72850
  grText.position = position;
72228
72851
  return grText;
@@ -72279,7 +72902,8 @@ function createGrTextFromCircuitJson({
72279
72902
  const grText = new GrText2({
72280
72903
  text: textElement.text,
72281
72904
  layer: kicadLayer,
72282
- effects: textEffects
72905
+ effects: textEffects,
72906
+ uuid: generateDeterministicUuid(textElement.pcb_silkscreen_text_id ?? textElement.text)
72283
72907
  });
72284
72908
  grText.position = position;
72285
72909
  return grText;
@@ -72298,6 +72922,44 @@ var normalizeOutlineCorners = (corners) => {
72298
72922
  }
72299
72923
  return dedupedCorners;
72300
72924
  };
72925
+ var EDGE_CUTS_WIDTH = 0.1;
72926
+ var appendGraphicLine = (kicadPcb, grLine) => {
72927
+ const graphicLines = kicadPcb.graphicLines;
72928
+ graphicLines.push(grLine);
72929
+ kicadPcb.graphicLines = graphicLines;
72930
+ };
72931
+ var appendGraphicCircle = (kicadPcb, grCircle) => {
72932
+ const graphicCircles = kicadPcb.graphicCircles;
72933
+ graphicCircles.push(grCircle);
72934
+ kicadPcb.graphicCircles = graphicCircles;
72935
+ };
72936
+ var appendGraphicPoly = (kicadPcb, grPoly) => {
72937
+ const graphicPolys = kicadPcb.graphicPolys;
72938
+ graphicPolys.push(grPoly);
72939
+ kicadPcb.graphicPolys = graphicPolys;
72940
+ };
72941
+ var rotatePointAroundOrigin = (point, rotationDegrees = 0) => {
72942
+ if (!rotationDegrees)
72943
+ return point;
72944
+ return applyToPoint22(rotate9(rotationDegrees * Math.PI / 180), point);
72945
+ };
72946
+ var getRectCutoutCorners = (cutout) => {
72947
+ const halfWidth = cutout.width / 2;
72948
+ const halfHeight = cutout.height / 2;
72949
+ const localCorners = [
72950
+ { x: -halfWidth, y: -halfHeight },
72951
+ { x: halfWidth, y: -halfHeight },
72952
+ { x: halfWidth, y: halfHeight },
72953
+ { x: -halfWidth, y: halfHeight }
72954
+ ];
72955
+ return localCorners.map((point) => {
72956
+ const rotatedPoint = rotatePointAroundOrigin(point, cutout.rotation);
72957
+ return {
72958
+ x: rotatedPoint.x + cutout.center.x,
72959
+ y: rotatedPoint.y + cutout.center.y
72960
+ };
72961
+ });
72962
+ };
72301
72963
  var AddGraphicsStage = class extends ConverterStage {
72302
72964
  _step() {
72303
72965
  const { kicadPcb, c2kMatPcb } = this.ctx;
@@ -72335,9 +72997,7 @@ var AddGraphicsStage = class extends ConverterStage {
72335
72997
  layer: kicadLayer,
72336
72998
  width: path7.stroke_width || 0.15
72337
72999
  });
72338
- const graphicLines = kicadPcb.graphicLines;
72339
- graphicLines.push(grLine);
72340
- kicadPcb.graphicLines = graphicLines;
73000
+ appendGraphicLine(kicadPcb, grLine);
72341
73001
  }
72342
73002
  }
72343
73003
  const standaloneSilkscreenTexts = this.ctx.db.pcb_silkscreen_text?.list().filter((text) => !text.pcb_component_id) || [];
@@ -72400,11 +73060,62 @@ var AddGraphicsStage = class extends ConverterStage {
72400
73060
  start: { x: start.x, y: start.y },
72401
73061
  end: { x: end.x, y: end.y },
72402
73062
  layer: "Edge.Cuts",
72403
- width: 0.1
73063
+ width: EDGE_CUTS_WIDTH
73064
+ });
73065
+ appendGraphicLine(kicadPcb, edgeLine);
73066
+ }
73067
+ }
73068
+ const pcbCutouts = this.ctx.db.pcb_cutout?.list() || [];
73069
+ for (const cutout of pcbCutouts) {
73070
+ if (cutout.shape === "circle") {
73071
+ const transformedCenter = applyToPoint22(c2kMatPcb, cutout.center);
73072
+ const transformedEnd = applyToPoint22(c2kMatPcb, {
73073
+ x: cutout.center.x + cutout.radius,
73074
+ y: cutout.center.y
72404
73075
  });
72405
- const graphicLines = kicadPcb.graphicLines;
72406
- graphicLines.push(edgeLine);
72407
- kicadPcb.graphicLines = graphicLines;
73076
+ appendGraphicCircle(kicadPcb, new GrCircle({
73077
+ center: transformedCenter,
73078
+ end: transformedEnd,
73079
+ layer: "Edge.Cuts",
73080
+ width: EDGE_CUTS_WIDTH
73081
+ }));
73082
+ } else if (cutout.shape === "polygon") {
73083
+ const corners = normalizeOutlineCorners(cutout.points);
73084
+ if (corners.length < 3)
73085
+ continue;
73086
+ appendGraphicPoly(kicadPcb, new GrPoly({
73087
+ points: corners.map((point) => applyToPoint22(c2kMatPcb, point)),
73088
+ layer: "Edge.Cuts",
73089
+ width: EDGE_CUTS_WIDTH,
73090
+ fill: false
73091
+ }));
73092
+ } else if (cutout.shape === "rect") {
73093
+ const corners = normalizeOutlineCorners(getRectCutoutCorners(cutout));
73094
+ if (corners.length < 3)
73095
+ continue;
73096
+ appendGraphicPoly(kicadPcb, new GrPoly({
73097
+ points: corners.map((point) => applyToPoint22(c2kMatPcb, point)),
73098
+ layer: "Edge.Cuts",
73099
+ width: EDGE_CUTS_WIDTH,
73100
+ fill: false
73101
+ }));
73102
+ } else if (cutout.shape === "path") {
73103
+ if (!cutout.route || cutout.route.length < 2)
73104
+ continue;
73105
+ for (let i = 0;i < cutout.route.length - 1; i++) {
73106
+ const startPoint = cutout.route[i];
73107
+ const endPoint = cutout.route[i + 1];
73108
+ if (!startPoint || !endPoint)
73109
+ continue;
73110
+ const transformedStart = applyToPoint22(c2kMatPcb, startPoint);
73111
+ const transformedEnd = applyToPoint22(c2kMatPcb, endPoint);
73112
+ appendGraphicLine(kicadPcb, new GrLine({
73113
+ start: transformedStart,
73114
+ end: transformedEnd,
73115
+ layer: "Edge.Cuts",
73116
+ width: EDGE_CUTS_WIDTH
73117
+ }));
73118
+ }
72408
73119
  }
72409
73120
  }
72410
73121
  this.finished = true;