calculate-packing 0.0.16 → 0.0.18

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
@@ -36,8 +36,10 @@ interface PackedComponent extends InputComponent {
36
36
  x: number;
37
37
  y: number;
38
38
  };
39
- /** Rotation in degrees (counterclockwise) */
39
+ /** @deprecated Rotation in degrees (counterclockwise) */
40
40
  ccwRotationOffset: number;
41
+ /** Rotation in degrees (counterclockwise) - output field */
42
+ ccwRotationDegrees?: number;
41
43
  pads: OutputPad[];
42
44
  }
43
45
  interface PackInput {
package/dist/index.js CHANGED
@@ -415,47 +415,6 @@ var getSegmentsFromPad = (pad, { padding = 0 } = {}) => {
415
415
  return segments;
416
416
  };
417
417
 
418
- // lib/PackSolver/computeGlobalCenter.ts
419
- function computeGlobalCenter(packedComponents) {
420
- if (!packedComponents.length) return { x: 0, y: 0 };
421
- const sum = packedComponents.reduce(
422
- (acc, component) => ({
423
- x: acc.x + component.center.x,
424
- y: acc.y + component.center.y
425
- }),
426
- { x: 0, y: 0 }
427
- );
428
- return {
429
- x: sum.x / packedComponents.length,
430
- y: sum.y / packedComponents.length
431
- };
432
- }
433
-
434
- // lib/PackSolver/findBestPointForDisconnected.ts
435
- function findBestPointForDisconnected({
436
- outlines,
437
- direction,
438
- packedComponents
439
- }) {
440
- const points = outlines.flatMap(
441
- (outline) => outline.map(([p1, p2]) => ({
442
- x: (p1.x + p2.x) / 2,
443
- y: (p1.y + p2.y) / 2
444
- }))
445
- );
446
- if (!points.length) return { x: 0, y: 0 };
447
- if (direction !== "nearest_to_center") {
448
- const extreme = direction === "left" || direction === "down" ? Math.min : Math.max;
449
- const key = direction === "left" || direction === "right" ? "x" : "y";
450
- const target = extreme(...points.map((p) => p[key]));
451
- return points.find((p) => p[key] === target);
452
- }
453
- const center = computeGlobalCenter(packedComponents);
454
- return points.reduce(
455
- (best, point) => Math.hypot(point.x - center.x, point.y - center.y) < Math.hypot(best.x - center.x, best.y - center.y) ? point : best
456
- );
457
- }
458
-
459
418
  // lib/PackSolver/setPackedComponentPadCenters.ts
460
419
  var setPackedComponentPadCenters = (packedComponent) => {
461
420
  packedComponent.pads = packedComponent.pads.map((pad) => {
@@ -477,66 +436,6 @@ var setPackedComponentPadCenters = (packedComponent) => {
477
436
  });
478
437
  };
479
438
 
480
- // lib/PackSolver/placeComponentAtPoint.ts
481
- function placeComponentAtPoint({
482
- component,
483
- point,
484
- candidateAngles,
485
- checkOverlap
486
- }) {
487
- const evaluatedPositionShadows = [];
488
- for (const angle of candidateAngles) {
489
- const pads = component.pads.map((pad) => {
490
- const rotatedOffset = rotatePoint(pad.offset, angle * Math.PI / 180);
491
- return {
492
- ...pad,
493
- absoluteCenter: {
494
- x: point.x + rotatedOffset.x,
495
- y: point.y + rotatedOffset.y
496
- }
497
- };
498
- });
499
- const candidate = {
500
- ...component,
501
- center: point,
502
- ccwRotationOffset: angle,
503
- pads
504
- };
505
- evaluatedPositionShadows.push(candidate);
506
- if (!checkOverlap(candidate)) {
507
- Object.assign(component, candidate);
508
- setPackedComponentPadCenters(component);
509
- return evaluatedPositionShadows;
510
- }
511
- }
512
- component.center = point;
513
- component.ccwRotationOffset = 0;
514
- setPackedComponentPadCenters(component);
515
- return evaluatedPositionShadows;
516
- }
517
-
518
- // lib/PackSolver/placeComponentDisconnected.ts
519
- function placeComponentDisconnected({
520
- component,
521
- outlines,
522
- direction,
523
- packedComponents,
524
- candidateAngles,
525
- checkOverlap
526
- }) {
527
- const targetPoint = findBestPointForDisconnected({
528
- outlines,
529
- direction,
530
- packedComponents
531
- });
532
- return placeComponentAtPoint({
533
- component,
534
- point: targetPoint,
535
- candidateAngles,
536
- checkOverlap
537
- });
538
- }
539
-
540
439
  // lib/PackSolver/sortComponentQueue.ts
541
440
  function sortComponentQueue({
542
441
  components,
@@ -799,26 +698,7 @@ var PhasedPackSolver = class extends BaseSolver {
799
698
  (id) => networkIdsInNewPackedComponent.has(id)
800
699
  )
801
700
  );
802
- if (sharedNetworkIds.size === 0) {
803
- this.phaseData.candidatePoints = [];
804
- this.phaseData.goodCandidates = [];
805
- const shadows = placeComponentDisconnected({
806
- component: newPackedComponent,
807
- outlines,
808
- direction: disconnectedPackDirection,
809
- packedComponents: this.packedComponents,
810
- candidateAngles: this.getCandidateAngles(newPackedComponent),
811
- checkOverlap: (comp) => this.checkOverlapWithPackedComponents(comp)
812
- });
813
- this.phaseData.selectedRotation = newPackedComponent;
814
- this.phaseData.rotationTrials = shadows.map((s) => ({
815
- ...s,
816
- cost: 0,
817
- anchorType: "center",
818
- hasOverlap: false
819
- }));
820
- return;
821
- }
701
+ const isDisconnected = sharedNetworkIds.size === 0;
822
702
  const candidatePoints = [];
823
703
  const goodCandidates = [];
824
704
  let smallestDistance = Number.POSITIVE_INFINITY;
@@ -827,19 +707,81 @@ var PhasedPackSolver = class extends BaseSolver {
827
707
  const [p1, p2] = segment;
828
708
  return `${p1.x.toFixed(6)},${p1.y.toFixed(6)}-${p2.x.toFixed(6)},${p2.y.toFixed(6)}`;
829
709
  };
710
+ const packedClusterCenter = this.packedComponents.length > 0 ? {
711
+ x: this.packedComponents.reduce((sum, c) => sum + c.center.x, 0) / this.packedComponents.length,
712
+ y: this.packedComponents.reduce((sum, c) => sum + c.center.y, 0) / this.packedComponents.length
713
+ } : { x: 0, y: 0 };
830
714
  const networkIdToAlreadyPackedSegments = /* @__PURE__ */ new Map();
831
- for (const sharedNetworkId of sharedNetworkIds) {
832
- const segments = [];
833
- for (const packedComponent of this.packedComponents) {
834
- for (const pad of packedComponent.pads) {
835
- if (pad.networkId === sharedNetworkId) {
836
- segments.push(...getSegmentsFromPad(pad));
715
+ if (!isDisconnected) {
716
+ for (const sharedNetworkId of sharedNetworkIds) {
717
+ const segments = [];
718
+ for (const packedComponent of this.packedComponents) {
719
+ for (const pad of packedComponent.pads) {
720
+ if (pad.networkId === sharedNetworkId) {
721
+ segments.push(...getSegmentsFromPad(pad));
722
+ }
837
723
  }
838
724
  }
725
+ networkIdToAlreadyPackedSegments.set(sharedNetworkId, segments);
839
726
  }
840
- networkIdToAlreadyPackedSegments.set(sharedNetworkId, segments);
841
727
  }
842
- if (packPlacementStrategy === "minimum_sum_distance_to_network" || packPlacementStrategy === "minimum_sum_squared_distance_to_network") {
728
+ if (isDisconnected) {
729
+ for (const outline of outlines) {
730
+ for (const outlineSegment of outline) {
731
+ const [p1, p2] = outlineSegment;
732
+ for (let t = 0; t <= 1; t += 0.1) {
733
+ const sampledPoint = {
734
+ x: p1.x + t * (p2.x - p1.x),
735
+ y: p1.y + t * (p2.y - p1.y)
736
+ };
737
+ let distance = 0;
738
+ if (disconnectedPackDirection === "nearest_to_center") {
739
+ distance = Math.sqrt(
740
+ Math.pow(sampledPoint.x - packedClusterCenter.x, 2) + Math.pow(sampledPoint.y - packedClusterCenter.y, 2)
741
+ );
742
+ } else if (disconnectedPackDirection === "left") {
743
+ distance = sampledPoint.x;
744
+ distance += 1e-3 * Math.abs(sampledPoint.y - packedClusterCenter.y);
745
+ } else if (disconnectedPackDirection === "right") {
746
+ distance = -sampledPoint.x;
747
+ distance += 1e-3 * Math.abs(sampledPoint.y - packedClusterCenter.y);
748
+ } else if (disconnectedPackDirection === "up") {
749
+ distance = -sampledPoint.y;
750
+ distance += 1e-3 * Math.abs(sampledPoint.x - packedClusterCenter.x);
751
+ } else if (disconnectedPackDirection === "down") {
752
+ distance = sampledPoint.y;
753
+ distance += 1e-3 * Math.abs(sampledPoint.x - packedClusterCenter.x);
754
+ }
755
+ const point = {
756
+ ...sampledPoint,
757
+ networkId: "disconnected",
758
+ distance
759
+ };
760
+ candidatePoints.push(point);
761
+ const segmentKey = getSegmentKey(outlineSegment);
762
+ const currentSegmentBest = segmentBestPoints.get(segmentKey);
763
+ if (!currentSegmentBest || distance < currentSegmentBest.distance) {
764
+ segmentBestPoints.set(segmentKey, {
765
+ point: {
766
+ ...sampledPoint,
767
+ networkId: "disconnected"
768
+ },
769
+ distance
770
+ });
771
+ }
772
+ if (distance < smallestDistance + 1e-6) {
773
+ if (distance < smallestDistance - 1e-6) {
774
+ goodCandidates.length = 0;
775
+ goodCandidates.push(point);
776
+ smallestDistance = distance;
777
+ } else {
778
+ goodCandidates.push(point);
779
+ }
780
+ }
781
+ }
782
+ }
783
+ }
784
+ } else if (packPlacementStrategy === "minimum_sum_distance_to_network" || packPlacementStrategy === "minimum_sum_squared_distance_to_network") {
843
785
  for (const outline of outlines) {
844
786
  for (const outlineSegment of outline) {
845
787
  const [p1, p2] = outlineSegment;
@@ -933,45 +875,47 @@ var PhasedPackSolver = class extends BaseSolver {
933
875
  }
934
876
  }
935
877
  }
936
- for (const sharedNetworkId of sharedNetworkIds) {
937
- for (const outline of outlines) {
938
- for (const outlineSegment of outline) {
939
- const [p1, p2] = outlineSegment;
940
- for (let t = 0; t <= 1; t += 0.2) {
941
- const sampledPoint = {
942
- x: p1.x + t * (p2.x - p1.x),
943
- y: p1.y + t * (p2.y - p1.y)
944
- };
945
- let distance = 0;
946
- const componentPadsOnNetwork = newPackedComponent.pads.filter(
947
- (p) => p.networkId === sharedNetworkId
948
- );
949
- for (const _ of componentPadsOnNetwork) {
950
- let minDist = Number.POSITIVE_INFINITY;
951
- for (const packedComponent of this.packedComponents) {
952
- for (const packedPad of packedComponent.pads) {
953
- if (packedPad.networkId === sharedNetworkId) {
954
- const dx = sampledPoint.x - packedPad.absoluteCenter.x;
955
- const dy = sampledPoint.y - packedPad.absoluteCenter.y;
956
- const dist = Math.sqrt(dx * dx + dy * dy);
957
- minDist = Math.min(minDist, dist);
878
+ if (!isDisconnected) {
879
+ for (const sharedNetworkId of sharedNetworkIds) {
880
+ for (const outline of outlines) {
881
+ for (const outlineSegment of outline) {
882
+ const [p1, p2] = outlineSegment;
883
+ for (let t = 0; t <= 1; t += 0.2) {
884
+ const sampledPoint = {
885
+ x: p1.x + t * (p2.x - p1.x),
886
+ y: p1.y + t * (p2.y - p1.y)
887
+ };
888
+ let distance = 0;
889
+ const componentPadsOnNetwork = newPackedComponent.pads.filter(
890
+ (p) => p.networkId === sharedNetworkId
891
+ );
892
+ for (const _ of componentPadsOnNetwork) {
893
+ let minDist = Number.POSITIVE_INFINITY;
894
+ for (const packedComponent of this.packedComponents) {
895
+ for (const packedPad of packedComponent.pads) {
896
+ if (packedPad.networkId === sharedNetworkId) {
897
+ const dx = sampledPoint.x - packedPad.absoluteCenter.x;
898
+ const dy = sampledPoint.y - packedPad.absoluteCenter.y;
899
+ const dist = Math.sqrt(dx * dx + dy * dy);
900
+ minDist = Math.min(minDist, dist);
901
+ }
958
902
  }
959
903
  }
904
+ distance += minDist;
905
+ }
906
+ const point = {
907
+ ...sampledPoint,
908
+ networkId: sharedNetworkId,
909
+ distance
910
+ };
911
+ candidatePoints.push(point);
912
+ if (distance < smallestDistance) {
913
+ smallestDistance = distance;
914
+ goodCandidates.length = 0;
915
+ goodCandidates.push(point);
916
+ } else if (distance === smallestDistance) {
917
+ goodCandidates.push(point);
960
918
  }
961
- distance += minDist;
962
- }
963
- const point = {
964
- ...sampledPoint,
965
- networkId: sharedNetworkId,
966
- distance
967
- };
968
- candidatePoints.push(point);
969
- if (distance < smallestDistance) {
970
- smallestDistance = distance;
971
- goodCandidates.length = 0;
972
- goodCandidates.push(point);
973
- } else if (distance === smallestDistance) {
974
- goodCandidates.push(point);
975
919
  }
976
920
  }
977
921
  }
@@ -1023,6 +967,14 @@ var PhasedPackSolver = class extends BaseSolver {
1023
967
  }
1024
968
  }
1025
969
  }
970
+ const isDisconnected = this.phaseData.goodCandidates.some(
971
+ (c) => c.networkId === "disconnected"
972
+ );
973
+ const packedClusterCenter = this.packedComponents.length > 0 ? {
974
+ x: this.packedComponents.reduce((sum, c) => sum + c.center.x, 0) / this.packedComponents.length,
975
+ y: this.packedComponents.reduce((sum, c) => sum + c.center.y, 0) / this.packedComponents.length
976
+ } : { x: 0, y: 0 };
977
+ const { disconnectedPackDirection = "nearest_to_center" } = this.packInput;
1026
978
  const useSquaredDistance = this.packInput.packPlacementStrategy === "minimum_sum_squared_distance_to_network";
1027
979
  for (const angle of candidateAngles) {
1028
980
  for (const point of this.phaseData.goodCandidates) {
@@ -1045,20 +997,40 @@ var PhasedPackSolver = class extends BaseSolver {
1045
997
  setPackedComponentPadCenters(trial);
1046
998
  const hasOverlap = this.checkOverlapWithPackedComponents(trial);
1047
999
  let cost = 0;
1048
- for (const pad of trial.pads) {
1049
- let minDist = Number.POSITIVE_INFINITY;
1050
- for (const packedComp of this.packedComponents) {
1051
- for (const packedPad of packedComp.pads) {
1052
- if (packedPad.networkId === pad.networkId) {
1053
- const dx = pad.absoluteCenter.x - packedPad.absoluteCenter.x;
1054
- const dy = pad.absoluteCenter.y - packedPad.absoluteCenter.y;
1055
- const dist = Math.sqrt(dx * dx + dy * dy);
1056
- minDist = Math.min(minDist, dist);
1000
+ if (isDisconnected) {
1001
+ if (disconnectedPackDirection === "nearest_to_center") {
1002
+ cost = Math.sqrt(
1003
+ Math.pow(componentCenter.x - packedClusterCenter.x, 2) + Math.pow(componentCenter.y - packedClusterCenter.y, 2)
1004
+ );
1005
+ } else if (disconnectedPackDirection === "left") {
1006
+ cost = componentCenter.x;
1007
+ cost += 1e-3 * Math.abs(componentCenter.y - packedClusterCenter.y);
1008
+ } else if (disconnectedPackDirection === "right") {
1009
+ cost = -componentCenter.x;
1010
+ cost += 1e-3 * Math.abs(componentCenter.y - packedClusterCenter.y);
1011
+ } else if (disconnectedPackDirection === "up") {
1012
+ cost = -componentCenter.y;
1013
+ cost += 1e-3 * Math.abs(componentCenter.x - packedClusterCenter.x);
1014
+ } else if (disconnectedPackDirection === "down") {
1015
+ cost = componentCenter.y;
1016
+ cost += 1e-3 * Math.abs(componentCenter.x - packedClusterCenter.x);
1017
+ }
1018
+ } else {
1019
+ for (const pad of trial.pads) {
1020
+ let minDist = Number.POSITIVE_INFINITY;
1021
+ for (const packedComp of this.packedComponents) {
1022
+ for (const packedPad of packedComp.pads) {
1023
+ if (packedPad.networkId === pad.networkId) {
1024
+ const dx = pad.absoluteCenter.x - packedPad.absoluteCenter.x;
1025
+ const dy = pad.absoluteCenter.y - packedPad.absoluteCenter.y;
1026
+ const dist = Math.sqrt(dx * dx + dy * dy);
1027
+ minDist = Math.min(minDist, dist);
1028
+ }
1057
1029
  }
1058
1030
  }
1059
- }
1060
- if (minDist < Number.POSITIVE_INFINITY) {
1061
- cost += useSquaredDistance ? minDist * minDist : minDist;
1031
+ if (minDist < Number.POSITIVE_INFINITY) {
1032
+ cost += useSquaredDistance ? minDist * minDist : minDist;
1033
+ }
1062
1034
  }
1063
1035
  }
1064
1036
  rotationTrials.push({
@@ -1075,20 +1047,40 @@ var PhasedPackSolver = class extends BaseSolver {
1075
1047
  setPackedComponentPadCenters(centerTrial);
1076
1048
  const centerHasOverlap = this.checkOverlapWithPackedComponents(centerTrial);
1077
1049
  let centerCost = 0;
1078
- for (const pad of centerTrial.pads) {
1079
- let minDist = Number.POSITIVE_INFINITY;
1080
- for (const packedComp of this.packedComponents) {
1081
- for (const packedPad of packedComp.pads) {
1082
- if (packedPad.networkId === pad.networkId) {
1083
- const dx = pad.absoluteCenter.x - packedPad.absoluteCenter.x;
1084
- const dy = pad.absoluteCenter.y - packedPad.absoluteCenter.y;
1085
- const dist = Math.sqrt(dx * dx + dy * dy);
1086
- minDist = Math.min(minDist, dist);
1050
+ if (isDisconnected) {
1051
+ if (disconnectedPackDirection === "nearest_to_center") {
1052
+ centerCost = Math.sqrt(
1053
+ Math.pow(centerTrial.center.x - packedClusterCenter.x, 2) + Math.pow(centerTrial.center.y - packedClusterCenter.y, 2)
1054
+ );
1055
+ } else if (disconnectedPackDirection === "left") {
1056
+ centerCost = centerTrial.center.x;
1057
+ centerCost += 1e-3 * Math.abs(centerTrial.center.y - packedClusterCenter.y);
1058
+ } else if (disconnectedPackDirection === "right") {
1059
+ centerCost = -centerTrial.center.x;
1060
+ centerCost += 1e-3 * Math.abs(centerTrial.center.y - packedClusterCenter.y);
1061
+ } else if (disconnectedPackDirection === "up") {
1062
+ centerCost = -centerTrial.center.y;
1063
+ centerCost += 1e-3 * Math.abs(centerTrial.center.x - packedClusterCenter.x);
1064
+ } else if (disconnectedPackDirection === "down") {
1065
+ centerCost = centerTrial.center.y;
1066
+ centerCost += 1e-3 * Math.abs(centerTrial.center.x - packedClusterCenter.x);
1067
+ }
1068
+ } else {
1069
+ for (const pad of centerTrial.pads) {
1070
+ let minDist = Number.POSITIVE_INFINITY;
1071
+ for (const packedComp of this.packedComponents) {
1072
+ for (const packedPad of packedComp.pads) {
1073
+ if (packedPad.networkId === pad.networkId) {
1074
+ const dx = pad.absoluteCenter.x - packedPad.absoluteCenter.x;
1075
+ const dy = pad.absoluteCenter.y - packedPad.absoluteCenter.y;
1076
+ const dist = Math.sqrt(dx * dx + dy * dy);
1077
+ minDist = Math.min(minDist, dist);
1078
+ }
1087
1079
  }
1088
1080
  }
1089
- }
1090
- if (minDist < Number.POSITIVE_INFINITY) {
1091
- centerCost += useSquaredDistance ? minDist * minDist : minDist;
1081
+ if (minDist < Number.POSITIVE_INFINITY) {
1082
+ centerCost += useSquaredDistance ? minDist * minDist : minDist;
1083
+ }
1092
1084
  }
1093
1085
  }
1094
1086
  rotationTrials.push({
@@ -1326,7 +1318,10 @@ var PhasedPackSolver = class extends BaseSolver {
1326
1318
  return [this.packInput];
1327
1319
  }
1328
1320
  getResult() {
1329
- return this.packedComponents;
1321
+ return this.packedComponents.map((component) => ({
1322
+ ...component,
1323
+ ccwRotationDegrees: component.ccwRotationOffset
1324
+ }));
1330
1325
  }
1331
1326
  /* ---------- small helpers ------------------------------------------------ */
1332
1327
  getCandidateAngles(c) {
@@ -1347,7 +1342,7 @@ var pack = (input) => {
1347
1342
  solver.solve();
1348
1343
  return {
1349
1344
  ...input,
1350
- components: solver.packedComponents
1345
+ components: solver.getResult()
1351
1346
  };
1352
1347
  };
1353
1348
 
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.16",
5
+ "version": "0.0.18",
6
6
  "description": "Calculate a packing layout with support for different strategy configurations",
7
7
  "scripts": {
8
8
  "start": "cosmos",