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 +2 -1
- package/dist/index.js +424 -46
- package/package.json +1 -1
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 {
|
|
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
|
-
|
|
446
|
-
for (const
|
|
447
|
-
for (const
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
outlineSegment
|
|
455
|
-
|
|
456
|
-
)
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
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
|
-
|
|
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