calculate-packing 0.0.9 → 0.0.11

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
@@ -43,7 +43,7 @@ interface PackInput {
43
43
  components: InputComponent[];
44
44
  minGap: number;
45
45
  packOrderStrategy: "largest_to_smallest";
46
- packPlacementStrategy: "shortest_connection_along_outline";
46
+ packPlacementStrategy: "shortest_connection_along_outline" | "minimum_sum_distance_to_network";
47
47
  disconnectedPackDirection?: "left" | "right" | "up" | "down" | "nearest_to_center";
48
48
  packFirst?: ComponentId[];
49
49
  }
@@ -140,6 +140,7 @@ declare class PackSolver extends BaseSolver {
140
140
  /** Visualize the current packing state – components are omitted, only the outline is shown. */
141
141
  visualize(): GraphicsObject;
142
142
  getResult(): PackedComponent[];
143
+ private computeSumDistanceForPosition;
143
144
  }
144
145
 
145
146
  declare const convertCircuitJsonToPackOutput: (circuitJson: CircuitJson, opts?: {
package/dist/index.js CHANGED
@@ -354,6 +354,271 @@ function computeNearestPointOnSegmentForSegmentSet(segmentA, segmentSet) {
354
354
 
355
355
  // lib/PackSolver/PackSolver.ts
356
356
  import { computeDistanceBetweenBoxes } from "@tscircuit/math-utils";
357
+
358
+ // lib/PackSolver/translationOptimizer.ts
359
+ function computeTranslationBounds(component, initialCenter, packedComponents, minGap) {
360
+ const componentBounds = getComponentBounds(component, 0);
361
+ const componentWidth = componentBounds.maxX - componentBounds.minX;
362
+ const componentHeight = componentBounds.maxY - componentBounds.minY;
363
+ let minX = Number.NEGATIVE_INFINITY;
364
+ let maxX = Number.POSITIVE_INFINITY;
365
+ let minY = Number.NEGATIVE_INFINITY;
366
+ let maxY = Number.POSITIVE_INFINITY;
367
+ for (const packedComp of packedComponents) {
368
+ const packedBounds = getComponentBounds(packedComp, 0);
369
+ const packedWidth = packedBounds.maxX - packedBounds.minX;
370
+ const packedHeight = packedBounds.maxY - packedBounds.minY;
371
+ const minCenterDistanceX = minGap + (componentWidth + packedWidth) / 2;
372
+ const minCenterDistanceY = minGap + (componentHeight + packedHeight) / 2;
373
+ const packedCenterX = packedComp.center.x;
374
+ const packedCenterY = packedComp.center.y;
375
+ const leftBound = packedCenterX - minCenterDistanceX;
376
+ const rightBound = packedCenterX + minCenterDistanceX;
377
+ const bottomBound = packedCenterY - minCenterDistanceY;
378
+ const topBound = packedCenterY + minCenterDistanceY;
379
+ if (Math.abs(packedCenterX - initialCenter.x) > Math.abs(packedCenterY - initialCenter.y)) {
380
+ if (packedCenterX < initialCenter.x) {
381
+ minX = Math.max(minX, rightBound);
382
+ } else {
383
+ maxX = Math.min(maxX, leftBound);
384
+ }
385
+ } else {
386
+ if (packedCenterY < initialCenter.y) {
387
+ minY = Math.max(minY, topBound);
388
+ } else {
389
+ maxY = Math.min(maxY, bottomBound);
390
+ }
391
+ }
392
+ }
393
+ const maxTranslation = 5;
394
+ minX = Math.max(minX, initialCenter.x - maxTranslation);
395
+ maxX = Math.min(maxX, initialCenter.x + maxTranslation);
396
+ minY = Math.max(minY, initialCenter.y - maxTranslation);
397
+ maxY = Math.min(maxY, initialCenter.y + maxTranslation);
398
+ return { minX, maxX, minY, maxY };
399
+ }
400
+ function calculateSumDistance(component, candidateCenter, packedComponents) {
401
+ const packedPads = packedComponents.flatMap((c) => c.pads);
402
+ const connectedPads = component.pads.filter(
403
+ (pad) => packedPads.some((pp) => pp.networkId === pad.networkId)
404
+ );
405
+ if (connectedPads.length === 0) return 0;
406
+ let sumDistance = 0;
407
+ for (const pad of connectedPads) {
408
+ const padAbsolutePos = {
409
+ x: candidateCenter.x + pad.offset.x,
410
+ y: candidateCenter.y + pad.offset.y
411
+ };
412
+ const sameNetPads = packedPads.filter(
413
+ (pp) => pp.networkId === pad.networkId
414
+ );
415
+ let minDistance = Number.POSITIVE_INFINITY;
416
+ for (const packedPad of sameNetPads) {
417
+ const distance = Math.hypot(
418
+ padAbsolutePos.x - packedPad.absoluteCenter.x,
419
+ padAbsolutePos.y - packedPad.absoluteCenter.y
420
+ );
421
+ if (distance < minDistance) {
422
+ minDistance = distance;
423
+ }
424
+ }
425
+ sumDistance += minDistance === Number.POSITIVE_INFINITY ? 0 : minDistance;
426
+ }
427
+ return sumDistance;
428
+ }
429
+ function checkOverlap(component, candidateCenter, packedComponents, minGap) {
430
+ const tempComponent = {
431
+ ...component,
432
+ center: candidateCenter,
433
+ pads: component.pads.map((p) => ({
434
+ ...p,
435
+ absoluteCenter: {
436
+ x: candidateCenter.x + p.offset.x,
437
+ y: candidateCenter.y + p.offset.y
438
+ }
439
+ }))
440
+ };
441
+ const componentBounds = getComponentBounds(tempComponent, 0);
442
+ const componentBox = {
443
+ center: {
444
+ x: (componentBounds.minX + componentBounds.maxX) / 2,
445
+ y: (componentBounds.minY + componentBounds.maxY) / 2
446
+ },
447
+ width: componentBounds.maxX - componentBounds.minX,
448
+ height: componentBounds.maxY - componentBounds.minY
449
+ };
450
+ for (const packedComp of packedComponents) {
451
+ const packedBounds = getComponentBounds(packedComp, 0);
452
+ const packedBox = {
453
+ center: {
454
+ x: (packedBounds.minX + packedBounds.maxX) / 2,
455
+ y: (packedBounds.minY + packedBounds.maxY) / 2
456
+ },
457
+ width: packedBounds.maxX - packedBounds.minX,
458
+ height: packedBounds.maxY - packedBounds.minY
459
+ };
460
+ const centerDistance = Math.hypot(
461
+ componentBox.center.x - packedBox.center.x,
462
+ componentBox.center.y - packedBox.center.y
463
+ );
464
+ const minRequiredDistance = minGap + (componentBox.width + packedBox.width) / 2;
465
+ if (centerDistance < minRequiredDistance) {
466
+ return true;
467
+ }
468
+ }
469
+ return false;
470
+ }
471
+ function clampToBounds(p, b) {
472
+ return {
473
+ x: Math.min(Math.max(p.x, b.minX), b.maxX),
474
+ y: Math.min(Math.max(p.y, b.minY), b.maxY)
475
+ };
476
+ }
477
+ function dist(a, b) {
478
+ const dx = a.x - b.x;
479
+ const dy = a.y - b.y;
480
+ return Math.hypot(dx, dy);
481
+ }
482
+ function backtrackToFeasible(from, to, isFeasible, maxTrials = 20) {
483
+ let lo = 0;
484
+ let hi = 1;
485
+ let best = null;
486
+ for (let i = 0; i < maxTrials; i++) {
487
+ const t = (lo + hi) / 2;
488
+ const cand = {
489
+ x: from.x + (to.x - from.x) * t,
490
+ y: from.y + (to.y - from.y) * t
491
+ };
492
+ if (isFeasible(cand)) {
493
+ best = cand;
494
+ lo = t;
495
+ } else {
496
+ hi = t;
497
+ }
498
+ }
499
+ return best ?? from;
500
+ }
501
+ function geometricMedianConstrained(targets, start, bounds, maxIter = 100, tol = 1e-4) {
502
+ let c = clampToBounds(start, bounds);
503
+ const eps = 1e-9;
504
+ for (let k = 0; k < maxIter; k++) {
505
+ for (const p of targets) {
506
+ if (dist(c, p) <= eps) return clampToBounds(p, bounds);
507
+ }
508
+ let numX = 0;
509
+ let numY = 0;
510
+ let den = 0;
511
+ for (const p of targets) {
512
+ const d = dist(c, p);
513
+ const w = 1 / Math.max(d, eps);
514
+ numX += p.x * w;
515
+ numY += p.y * w;
516
+ den += w;
517
+ }
518
+ if (den === 0) break;
519
+ const next = clampToBounds({ x: numX / den, y: numY / den }, bounds);
520
+ if (dist(next, c) < tol) return next;
521
+ c = next;
522
+ }
523
+ return c;
524
+ }
525
+ function optimizeTranslationForMinimumSum(context) {
526
+ const { component, initialCenter, packedComponents, minGap } = context;
527
+ const bounds = computeTranslationBounds(
528
+ component,
529
+ initialCenter,
530
+ packedComponents,
531
+ minGap
532
+ );
533
+ if (bounds.minX >= bounds.maxX || bounds.minY >= bounds.maxY) {
534
+ return initialCenter;
535
+ }
536
+ const packedPads = packedComponents.flatMap((c) => c.pads);
537
+ const connectedPads = component.pads.filter(
538
+ (pad) => packedPads.some((pp) => pp.networkId === pad.networkId)
539
+ );
540
+ if (connectedPads.length === 0) return initialCenter;
541
+ const assignTargets = (center2) => {
542
+ const results = [];
543
+ for (const pad of connectedPads) {
544
+ const padAbs = { x: center2.x + pad.offset.x, y: center2.y + pad.offset.y };
545
+ let best = null;
546
+ for (let j = 0; j < packedPads.length; j++) {
547
+ const pp = packedPads[j];
548
+ if (!pp || pp.networkId !== pad.networkId) continue;
549
+ const d = Math.hypot(
550
+ padAbs.x - pp.absoluteCenter.x,
551
+ padAbs.y - pp.absoluteCenter.y
552
+ );
553
+ if (!best || d < dist(padAbs, best.p)) {
554
+ best = {
555
+ p: {
556
+ x: pp.absoluteCenter.x - pad.offset.x,
557
+ y: pp.absoluteCenter.y - pad.offset.y
558
+ },
559
+ key: `${pad.networkId}:${j}`
560
+ };
561
+ }
562
+ }
563
+ if (best) results.push({ targets: best.p, key: best.key });
564
+ }
565
+ return results;
566
+ };
567
+ let center = clampToBounds(initialCenter, bounds);
568
+ let prevAssignSignature = "";
569
+ const maxOuterIters = 30;
570
+ const tolMove = 1e-3;
571
+ if (checkOverlap(component, center, packedComponents, minGap)) {
572
+ const mid = {
573
+ x: (bounds.minX + bounds.maxX) / 2,
574
+ y: (bounds.minY + bounds.maxY) / 2
575
+ };
576
+ center = backtrackToFeasible(
577
+ center,
578
+ mid,
579
+ (p) => !checkOverlap(component, p, packedComponents, minGap)
580
+ );
581
+ }
582
+ for (let iter = 0; iter < maxOuterIters; iter++) {
583
+ const assignments = assignTargets(center);
584
+ if (assignments.length === 0) return initialCenter;
585
+ const signature = assignments.map((a) => a.key).join("|");
586
+ const targets = assignments.map((a) => a.targets);
587
+ const proposed = geometricMedianConstrained(
588
+ targets,
589
+ center,
590
+ bounds,
591
+ 100,
592
+ 1e-4
593
+ );
594
+ let next = proposed;
595
+ if (checkOverlap(component, next, packedComponents, minGap)) {
596
+ next = backtrackToFeasible(
597
+ center,
598
+ proposed,
599
+ (p) => !checkOverlap(component, p, packedComponents, minGap)
600
+ );
601
+ }
602
+ if (signature === prevAssignSignature && dist(next, center) < tolMove) {
603
+ center = next;
604
+ break;
605
+ }
606
+ prevAssignSignature = signature;
607
+ center = next;
608
+ }
609
+ const initCost = calculateSumDistance(
610
+ component,
611
+ initialCenter,
612
+ packedComponents
613
+ );
614
+ const finalCost = calculateSumDistance(component, center, packedComponents);
615
+ if (finalCost > initCost && !checkOverlap(component, initialCenter, packedComponents, minGap)) {
616
+ return initialCenter;
617
+ }
618
+ return center;
619
+ }
620
+
621
+ // lib/PackSolver/PackSolver.ts
357
622
  var PackSolver = class extends BaseSolver {
358
623
  packInput;
359
624
  unpackedComponentQueue;
@@ -365,8 +630,19 @@ var PackSolver = class extends BaseSolver {
365
630
  this.packInput = input;
366
631
  }
367
632
  _setup() {
368
- const { components, packOrderStrategy } = this.packInput;
633
+ const { components, packOrderStrategy, packFirst = [] } = this.packInput;
634
+ const packFirstMap = /* @__PURE__ */ new Map();
635
+ packFirst.forEach((componentId, index) => {
636
+ packFirstMap.set(componentId, index);
637
+ });
369
638
  this.unpackedComponentQueue = [...components].sort((a, b) => {
639
+ const aPackFirstIndex = packFirstMap.get(a.componentId);
640
+ const bPackFirstIndex = packFirstMap.get(b.componentId);
641
+ if (aPackFirstIndex !== void 0 && bPackFirstIndex !== void 0) {
642
+ return aPackFirstIndex - bPackFirstIndex;
643
+ }
644
+ if (aPackFirstIndex !== void 0) return -1;
645
+ if (bPackFirstIndex !== void 0) return 1;
370
646
  if (packOrderStrategy === "largest_to_smallest") {
371
647
  return b.pads.length - a.pads.length;
372
648
  }
@@ -376,7 +652,11 @@ var PackSolver = class extends BaseSolver {
376
652
  }
377
653
  _step() {
378
654
  if (this.solved) return;
379
- const { minGap = 0, disconnectedPackDirection = "nearest_to_center" } = this.packInput;
655
+ const {
656
+ minGap = 0,
657
+ disconnectedPackDirection = "nearest_to_center",
658
+ packPlacementStrategy = "shortest_connection_along_outline"
659
+ } = this.packInput;
380
660
  if (this.unpackedComponentQueue.length === 0) {
381
661
  this.solved = true;
382
662
  return;
@@ -442,32 +722,67 @@ var PackSolver = class extends BaseSolver {
442
722
  }
443
723
  let smallestDistance = Number.POSITIVE_INFINITY;
444
724
  let bestPoints = [];
445
- for (const outline of outlines) {
446
- for (const outlineSegment of outline) {
447
- for (const sharedNetworkId of sharedNetworkIds) {
448
- const alreadyPackedSegments = networkIdToAlreadyPackedSegments.get(sharedNetworkId);
449
- if (!alreadyPackedSegments) continue;
450
- const {
451
- nearestPoint: nearestPointOnOutlineToAlreadyPackedSegments,
452
- dist: outlineToAlreadyPackedSegmentsDist
453
- } = computeNearestPointOnSegmentForSegmentSet(
454
- outlineSegment,
455
- alreadyPackedSegments
456
- );
457
- if (outlineToAlreadyPackedSegmentsDist < smallestDistance + 1e-6) {
458
- if (outlineToAlreadyPackedSegmentsDist < smallestDistance - 1e-6) {
459
- bestPoints = [
460
- {
725
+ if (packPlacementStrategy === "minimum_sum_distance_to_network") {
726
+ for (const outline of outlines) {
727
+ for (const outlineSegment of outline) {
728
+ const samplePoints = [
729
+ outlineSegment[0],
730
+ {
731
+ x: (outlineSegment[0].x + outlineSegment[1].x) / 2,
732
+ y: (outlineSegment[0].y + outlineSegment[1].y) / 2
733
+ },
734
+ outlineSegment[1]
735
+ ];
736
+ for (const samplePoint of samplePoints) {
737
+ for (const sharedNetworkId of sharedNetworkIds) {
738
+ const sumDistance = this.computeSumDistanceForPosition(
739
+ newPackedComponent,
740
+ samplePoint,
741
+ sharedNetworkId
742
+ );
743
+ if (sumDistance < smallestDistance + 1e-6) {
744
+ if (sumDistance < smallestDistance - 1e-6) {
745
+ bestPoints = [{ ...samplePoint, networkId: sharedNetworkId }];
746
+ smallestDistance = sumDistance;
747
+ } else {
748
+ bestPoints.push({
749
+ ...samplePoint,
750
+ networkId: sharedNetworkId
751
+ });
752
+ }
753
+ }
754
+ }
755
+ }
756
+ }
757
+ }
758
+ } else {
759
+ for (const outline of outlines) {
760
+ for (const outlineSegment of outline) {
761
+ for (const sharedNetworkId of sharedNetworkIds) {
762
+ const alreadyPackedSegments = networkIdToAlreadyPackedSegments.get(sharedNetworkId);
763
+ if (!alreadyPackedSegments) continue;
764
+ const {
765
+ nearestPoint: nearestPointOnOutlineToAlreadyPackedSegments,
766
+ dist: outlineToAlreadyPackedSegmentsDist
767
+ } = computeNearestPointOnSegmentForSegmentSet(
768
+ outlineSegment,
769
+ alreadyPackedSegments
770
+ );
771
+ if (outlineToAlreadyPackedSegmentsDist < smallestDistance + 1e-6) {
772
+ if (outlineToAlreadyPackedSegmentsDist < smallestDistance - 1e-6) {
773
+ bestPoints = [
774
+ {
775
+ ...nearestPointOnOutlineToAlreadyPackedSegments,
776
+ networkId: sharedNetworkId
777
+ }
778
+ ];
779
+ smallestDistance = outlineToAlreadyPackedSegmentsDist;
780
+ } else {
781
+ bestPoints.push({
461
782
  ...nearestPointOnOutlineToAlreadyPackedSegments,
462
783
  networkId: sharedNetworkId
463
- }
464
- ];
465
- smallestDistance = outlineToAlreadyPackedSegmentsDist;
466
- } else {
467
- bestPoints.push({
468
- ...nearestPointOnOutlineToAlreadyPackedSegments,
469
- networkId: sharedNetworkId
470
- });
784
+ });
785
+ }
471
786
  }
472
787
  }
473
788
  }
@@ -510,25 +825,65 @@ var PackSolver = class extends BaseSolver {
510
825
  ccwRotationOffset: angle,
511
826
  pads: transformedPads
512
827
  };
513
- this.lastEvaluatedPositionShadows?.push(tempComponent);
828
+ this.lastEvaluatedPositionShadows?.push({ ...tempComponent });
514
829
  if (this.checkOverlapWithPackedComponents(tempComponent)) continue;
515
830
  let cost = 0;
516
- for (const tp of transformedPads) {
517
- const sameNetPads = packedPads.filter(
518
- (pp) => pp.networkId === tp.networkId
519
- );
520
- if (!sameNetPads.length) continue;
521
- let bestD = Infinity;
522
- for (const pp of sameNetPads) {
523
- const dx = tp.absoluteCenter.x - pp.absoluteCenter.x;
524
- const dy = tp.absoluteCenter.y - pp.absoluteCenter.y;
525
- const d = Math.hypot(dx, dy);
526
- if (d < bestD) bestD = d;
831
+ if (packPlacementStrategy === "minimum_sum_distance_to_network") {
832
+ const optimizedCenter = optimizeTranslationForMinimumSum({
833
+ component: tempComponent,
834
+ initialCenter: candidateCenter,
835
+ packedComponents: this.packedComponents,
836
+ minGap
837
+ });
838
+ const optimizedTransformedPads = newPackedComponent.pads.map((p) => {
839
+ const ro = rotatePoint(p.offset, angle);
840
+ return {
841
+ ...p,
842
+ absoluteCenter: {
843
+ x: optimizedCenter.x + ro.x,
844
+ y: optimizedCenter.y + ro.y
845
+ }
846
+ };
847
+ });
848
+ tempComponent.center = optimizedCenter;
849
+ tempComponent.pads = optimizedTransformedPads;
850
+ if (optimizedCenter.x !== candidateCenter.x || optimizedCenter.y !== candidateCenter.y) {
851
+ this.lastEvaluatedPositionShadows?.push({ ...tempComponent });
852
+ }
853
+ if (this.checkOverlapWithPackedComponents(tempComponent)) continue;
854
+ for (const tp of optimizedTransformedPads) {
855
+ const sameNetPads = packedPads.filter(
856
+ (pp) => pp.networkId === tp.networkId
857
+ );
858
+ if (!sameNetPads.length) continue;
859
+ let bestD = Infinity;
860
+ for (const pp of sameNetPads) {
861
+ const dx = tp.absoluteCenter.x - pp.absoluteCenter.x;
862
+ const dy = tp.absoluteCenter.y - pp.absoluteCenter.y;
863
+ const d = Math.hypot(dx, dy);
864
+ if (d < bestD) bestD = d;
865
+ }
866
+ cost += bestD === Infinity ? 0 : bestD;
867
+ }
868
+ } else {
869
+ for (const tp of transformedPads) {
870
+ const sameNetPads = packedPads.filter(
871
+ (pp) => pp.networkId === tp.networkId
872
+ );
873
+ if (!sameNetPads.length) continue;
874
+ let bestD = Infinity;
875
+ for (const pp of sameNetPads) {
876
+ const dx = tp.absoluteCenter.x - pp.absoluteCenter.x;
877
+ const dy = tp.absoluteCenter.y - pp.absoluteCenter.y;
878
+ const d = Math.hypot(dx, dy);
879
+ if (d < bestD) bestD = d;
880
+ }
881
+ cost += bestD;
527
882
  }
528
- cost += bestD;
529
883
  }
530
884
  if (!bestCandidate || cost < bestCandidate.cost) {
531
- bestCandidate = { center: candidateCenter, angle, cost };
885
+ const finalCenter = packPlacementStrategy === "minimum_sum_distance_to_network" ? tempComponent.center : candidateCenter;
886
+ bestCandidate = { center: finalCenter, angle, cost };
532
887
  }
533
888
  }
534
889
  if (bestCandidate) {
@@ -599,12 +954,6 @@ var PackSolver = class extends BaseSolver {
599
954
  }))
600
955
  );
601
956
  if (!pts.length) return { x: 0, y: 0 };
602
- const cmp = {
603
- left: (p, v) => p.x < v,
604
- right: (p, v) => p.x > v,
605
- down: (p, v) => p.y < v,
606
- up: (p, v) => p.y > v
607
- };
608
957
  if (dir !== "nearest_to_center") {
609
958
  const extreme = dir === "left" || dir === "down" ? Math.min : Math.max;
610
959
  const key = dir === "left" || dir === "right" ? "x" : "y";
@@ -707,6 +1056,35 @@ d=${this.lastBestPointsResult.distance}`
707
1056
  getResult() {
708
1057
  return this.packedComponents;
709
1058
  }
1059
+ computeSumDistanceForPosition(component, position, targetNetworkId) {
1060
+ const componentPadsOnNetwork = component.pads.filter(
1061
+ (p) => p.networkId === targetNetworkId
1062
+ );
1063
+ if (componentPadsOnNetwork.length === 0) return 0;
1064
+ const packedPadsOnNetwork = this.packedComponents.flatMap(
1065
+ (c) => c.pads.filter((p) => p.networkId === targetNetworkId)
1066
+ );
1067
+ if (packedPadsOnNetwork.length === 0) return 0;
1068
+ let sumDistance = 0;
1069
+ for (const componentPad of componentPadsOnNetwork) {
1070
+ const padPosition = {
1071
+ x: position.x + componentPad.offset.x,
1072
+ y: position.y + componentPad.offset.y
1073
+ };
1074
+ let minDistance = Number.POSITIVE_INFINITY;
1075
+ for (const packedPad of packedPadsOnNetwork) {
1076
+ const distance = Math.hypot(
1077
+ padPosition.x - packedPad.absoluteCenter.x,
1078
+ padPosition.y - packedPad.absoluteCenter.y
1079
+ );
1080
+ if (distance < minDistance) {
1081
+ minDistance = distance;
1082
+ }
1083
+ }
1084
+ sumDistance += minDistance === Number.POSITIVE_INFINITY ? 0 : minDistance;
1085
+ }
1086
+ return sumDistance;
1087
+ }
710
1088
  };
711
1089
 
712
1090
  // lib/pack.ts
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.9",
5
+ "version": "0.0.11",
6
6
  "description": "Calculate a packing layout with support for different strategy configurations",
7
7
  "scripts": {
8
8
  "start": "cosmos",