calculate-packing 0.0.7 → 0.0.9

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
@@ -142,7 +142,9 @@ declare class PackSolver extends BaseSolver {
142
142
  getResult(): PackedComponent[];
143
143
  }
144
144
 
145
- declare const convertCircuitJsonToPackOutput: (circuitJson: CircuitJson) => PackOutput;
145
+ declare const convertCircuitJsonToPackOutput: (circuitJson: CircuitJson, opts?: {
146
+ source_group_id?: string;
147
+ }) => PackOutput;
146
148
 
147
149
  declare const getGraphicsFromPackOutput: (packOutput: PackOutput) => GraphicsObject;
148
150
 
package/dist/index.js CHANGED
@@ -719,17 +719,187 @@ var pack = (input) => {
719
719
  };
720
720
  };
721
721
 
722
- // lib/testing/convertCircuitJsonToPackOutput.ts
723
- import { cju } from "@tscircuit/circuit-json-util";
724
- var convertCircuitJsonToPackOutput = (circuitJson) => {
722
+ // lib/plumbing/convertCircuitJsonToPackOutput.ts
723
+ import { cju, getCircuitJsonTree } from "@tscircuit/circuit-json-util";
724
+
725
+ // lib/plumbing/extractPadInfos.ts
726
+ var extractPadInfos = (pcbComponent, db, getNetworkId) => {
727
+ const out = [];
728
+ const pushPad = ({
729
+ padId,
730
+ pcbPortId,
731
+ sx,
732
+ sy,
733
+ x,
734
+ y
735
+ }) => out.push({
736
+ padId,
737
+ networkId: getNetworkId(pcbPortId),
738
+ size: { x: sx, y: sy },
739
+ absoluteCenter: { x, y }
740
+ });
741
+ for (const ph of db.pcb_plated_hole.list({
742
+ pcb_component_id: pcbComponent.pcb_component_id
743
+ })) {
744
+ switch (ph.shape) {
745
+ case "circle": {
746
+ pushPad({
747
+ padId: ph.pcb_plated_hole_id,
748
+ pcbPortId: ph.pcb_port_id,
749
+ sx: ph.outer_diameter ?? ph.hole_diameter ?? 0,
750
+ sy: ph.outer_diameter ?? ph.hole_diameter ?? 0,
751
+ x: ph.x,
752
+ y: ph.y
753
+ });
754
+ break;
755
+ }
756
+ case "oval": {
757
+ pushPad({
758
+ padId: ph.pcb_plated_hole_id,
759
+ pcbPortId: ph.pcb_port_id,
760
+ sx: ph.outer_width,
761
+ sy: ph.outer_height,
762
+ x: ph.x,
763
+ y: ph.y
764
+ });
765
+ break;
766
+ }
767
+ case "circular_hole_with_rect_pad": {
768
+ pushPad({
769
+ padId: ph.pcb_plated_hole_id,
770
+ pcbPortId: ph.pcb_port_id,
771
+ sx: ph.rect_pad_width,
772
+ sy: ph.rect_pad_height,
773
+ x: ph.x,
774
+ y: ph.y
775
+ });
776
+ break;
777
+ }
778
+ case "pill": {
779
+ pushPad({
780
+ padId: ph.pcb_plated_hole_id,
781
+ pcbPortId: ph.pcb_port_id,
782
+ sx: ph.outer_width,
783
+ sy: ph.outer_height,
784
+ x: ph.x,
785
+ y: ph.y
786
+ });
787
+ break;
788
+ }
789
+ case "pill_hole_with_rect_pad": {
790
+ pushPad({
791
+ padId: ph.pcb_plated_hole_id,
792
+ pcbPortId: ph.pcb_port_id,
793
+ sx: ph.rect_pad_width,
794
+ sy: ph.rect_pad_height,
795
+ x: ph.x,
796
+ y: ph.y
797
+ });
798
+ break;
799
+ }
800
+ }
801
+ console.warn(`Unsupported plated hole shape ${ph.shape}`);
802
+ }
803
+ for (const sp of db.pcb_smtpad.list({
804
+ pcb_component_id: pcbComponent.pcb_component_id
805
+ })) {
806
+ switch (sp.shape) {
807
+ case "rect": {
808
+ pushPad({
809
+ padId: sp.pcb_smtpad_id,
810
+ pcbPortId: sp.pcb_port_id,
811
+ sx: sp.width ?? 0,
812
+ sy: sp.height ?? 0,
813
+ x: sp.x,
814
+ y: sp.y
815
+ });
816
+ break;
817
+ }
818
+ case "circle": {
819
+ pushPad({
820
+ padId: sp.pcb_smtpad_id,
821
+ pcbPortId: sp.pcb_port_id,
822
+ sx: sp.radius ?? 0,
823
+ sy: sp.radius ?? 0,
824
+ x: sp.x,
825
+ y: sp.y
826
+ });
827
+ break;
828
+ }
829
+ case "pill": {
830
+ pushPad({
831
+ padId: sp.pcb_smtpad_id,
832
+ pcbPortId: sp.pcb_port_id,
833
+ sx: sp.width ?? 0,
834
+ sy: sp.height ?? 0,
835
+ x: sp.x,
836
+ y: sp.y
837
+ });
838
+ break;
839
+ }
840
+ default: {
841
+ console.warn(
842
+ `smtpad shape ${sp.shape} pads are not supported in pack layout yet`
843
+ );
844
+ break;
845
+ }
846
+ }
847
+ }
848
+ return out;
849
+ };
850
+
851
+ // lib/plumbing/convertCircuitJsonToPackOutput.ts
852
+ var buildPackedComponent = (pcbComponents, componentId, db, getNetworkId) => {
853
+ const padInfos = pcbComponents.flatMap(
854
+ (pc) => extractPadInfos(pc, db, getNetworkId)
855
+ );
856
+ let minX = Infinity;
857
+ let minY = Infinity;
858
+ let maxX = -Infinity;
859
+ let maxY = -Infinity;
860
+ for (const p of padInfos) {
861
+ minX = Math.min(minX, p.absoluteCenter.x - p.size.x / 2);
862
+ maxX = Math.max(maxX, p.absoluteCenter.x + p.size.x / 2);
863
+ minY = Math.min(minY, p.absoluteCenter.y - p.size.y / 2);
864
+ maxY = Math.max(maxY, p.absoluteCenter.y + p.size.y / 2);
865
+ }
866
+ const center = { x: (minX + maxX) / 2, y: (minY + maxY) / 2 };
867
+ const pads = padInfos.map((p) => ({
868
+ padId: p.padId,
869
+ networkId: p.networkId,
870
+ type: "rect",
871
+ size: p.size,
872
+ absoluteCenter: p.absoluteCenter,
873
+ offset: {
874
+ x: p.absoluteCenter.x - center.x,
875
+ y: p.absoluteCenter.y - center.y
876
+ }
877
+ }));
878
+ return {
879
+ componentId,
880
+ center,
881
+ ccwRotationOffset: 0,
882
+ pads
883
+ };
884
+ };
885
+ var collectPcbComponents = (node, db) => {
886
+ if (node.nodeType === "component") {
887
+ const pcbId = node.otherChildElements[0]?.pcb_component_id;
888
+ return pcbId ? [db.pcb_component.get(pcbId)] : [];
889
+ }
890
+ return node.childNodes.flatMap((n) => collectPcbComponents(n, db));
891
+ };
892
+ var convertCircuitJsonToPackOutput = (circuitJson, opts = {}) => {
725
893
  const packOutput = {
726
894
  components: [],
727
895
  minGap: 0,
728
896
  packOrderStrategy: "largest_to_smallest",
729
897
  packPlacementStrategy: "shortest_connection_along_outline"
730
898
  };
899
+ const tree = getCircuitJsonTree(circuitJson, {
900
+ source_group_id: opts.source_group_id
901
+ });
731
902
  const db = cju(circuitJson);
732
- const pcbComponents = db.pcb_component.list();
733
903
  let unnamedCounter = 0;
734
904
  const getNetworkId = (pcbPortId) => {
735
905
  if (pcbPortId) {
@@ -743,68 +913,29 @@ var convertCircuitJsonToPackOutput = (circuitJson) => {
743
913
  }
744
914
  return `unnamed${unnamedCounter++}`;
745
915
  };
746
- for (const pcbComponent of pcbComponents) {
747
- const pads = [];
748
- const platedHoles = db.pcb_plated_hole.list({
749
- pcb_component_id: pcbComponent.pcb_component_id
750
- });
751
- for (const platedHole of platedHoles) {
752
- const sx = platedHole.rect_pad_width ?? platedHole.outer_diameter ?? platedHole.hole_diameter ?? 0;
753
- const sy = platedHole.rect_pad_height ?? platedHole.outer_diameter ?? platedHole.hole_diameter ?? 0;
754
- const networkId = getNetworkId(platedHole.pcb_port_id);
755
- const pad = {
756
- padId: platedHole.pcb_plated_hole_id,
757
- networkId,
758
- type: "rect",
759
- offset: {
760
- x: platedHole.x - pcbComponent.center.x,
761
- y: platedHole.y - pcbComponent.center.y
762
- },
763
- size: { x: sx, y: sy },
764
- absoluteCenter: {
765
- x: platedHole.x,
766
- y: platedHole.y
767
- }
768
- };
769
- pads.push(pad);
770
- }
771
- const smtPads = db.pcb_smtpad.list({
772
- pcb_component_id: pcbComponent.pcb_component_id
773
- });
774
- for (const smtPad of smtPads) {
775
- if (smtPad.shape === "polygon") {
776
- throw new Error("Polygon pads are not supported in pack layout yet");
777
- }
778
- const networkId = getNetworkId(smtPad.pcb_port_id);
779
- const pad = {
780
- padId: smtPad.pcb_smtpad_id,
781
- networkId,
782
- type: "rect",
783
- offset: {
784
- x: smtPad.x - pcbComponent.center.x,
785
- y: smtPad.y - pcbComponent.center.y
786
- },
787
- size: {
788
- x: smtPad.width ?? 0,
789
- y: smtPad.height ?? 0
790
- },
791
- absoluteCenter: {
792
- x: smtPad.x,
793
- y: smtPad.y
794
- }
795
- };
796
- pads.push(pad);
916
+ const topLevelNodes = tree.childNodes ?? [];
917
+ for (const node of topLevelNodes) {
918
+ if (node.nodeType === "component") {
919
+ const pcbComponent = node.otherChildElements.find(
920
+ (e) => e.type === "pcb_component"
921
+ );
922
+ if (!pcbComponent) continue;
923
+ packOutput.components.push(
924
+ buildPackedComponent(
925
+ [pcbComponent],
926
+ pcbComponent.pcb_component_id,
927
+ db,
928
+ getNetworkId
929
+ )
930
+ );
931
+ } else if (node.nodeType === "group") {
932
+ const pcbComps = collectPcbComponents(node, db);
933
+ if (!pcbComps.length) continue;
934
+ const compId = node.sourceGroup?.source_group_id ?? node.sourceGroup?.name ?? `group_${packOutput.components.length}`;
935
+ packOutput.components.push(
936
+ buildPackedComponent(pcbComps, compId, db, getNetworkId)
937
+ );
797
938
  }
798
- const packedComponent = {
799
- componentId: pcbComponent.pcb_component_id,
800
- pads,
801
- center: {
802
- x: pcbComponent.center.x,
803
- y: pcbComponent.center.y
804
- },
805
- ccwRotationOffset: 0
806
- };
807
- packOutput.components.push(packedComponent);
808
939
  }
809
940
  return packOutput;
810
941
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "calculate-packing",
3
3
  "main": "dist/index.js",
4
4
  "type": "module",
5
- "version": "0.0.7",
5
+ "version": "0.0.9",
6
6
  "description": "Calculate a packing layout with support for different strategy configurations",
7
7
  "scripts": {
8
8
  "start": "cosmos",
@@ -18,7 +18,7 @@
18
18
  "@biomejs/biome": "^2.1.1",
19
19
  "@flatten-js/core": "^1.6.2",
20
20
  "@react-hook/resize-observer": "^2.0.2",
21
- "@tscircuit/circuit-json-util": "^0.0.52",
21
+ "@tscircuit/circuit-json-util": "^0.0.57",
22
22
  "@tscircuit/footprinter": "^0.0.203",
23
23
  "@tscircuit/math-utils": "^0.0.19",
24
24
  "@types/bun": "latest",
@@ -31,6 +31,7 @@
31
31
  "react-cosmos": "^7.0.0",
32
32
  "react-cosmos-plugin-vite": "^7.0.0",
33
33
  "react-dom": "^19.1.0",
34
+ "tscircuit": "^0.0.562",
34
35
  "tsup": "^8.5.0",
35
36
  "vite": "^7.0.5"
36
37
  },