@tscircuit/schematic-trace-solver 0.0.144 → 0.0.146
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 +4 -0
- package/dist/index.js +448 -61
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +30 -8
- package/lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts +530 -0
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +14 -5
- package/lib/solvers/MspConnectionPairSolver/isLabeledPeripheralConnection.ts +46 -10
- package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +7 -0
- package/package.json +1 -1
- package/tests/assets/inline-net-label-anchored-label-clearance.json +56 -0
- package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +10 -30
- package/tests/repros/assets/repro-tida010076-page02.input.json +2 -0
- package/tests/repros/repro-tida010076-page02.test.ts +31 -0
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-anchored-label-clearance.snap.svg +54 -0
- package/tests/solvers/InlineNetLabelSolver/inline-net-label-anchored-label-clearance.test.ts +28 -0
- package/tests/solvers/InlineNetLabelSolver/push-anchored-net-labels-away.test.ts +227 -0
- package/tests/solvers/MspConnectionPairSolver/msp-connection-pair-solver-direct-connection-distance.test.ts +80 -0
package/dist/index.d.ts
CHANGED
|
@@ -146,6 +146,8 @@ type MspConnectionPair = {
|
|
|
146
146
|
dcConnNetId: string;
|
|
147
147
|
globalConnNetId: string;
|
|
148
148
|
userNetId?: string;
|
|
149
|
+
/** The trace replaces fallback labels that could not fit between its pins. */
|
|
150
|
+
suppressNetLabel?: boolean;
|
|
149
151
|
pins: [InputPin & {
|
|
150
152
|
chipId: string;
|
|
151
153
|
}, InputPin & {
|
|
@@ -1248,6 +1250,7 @@ declare class InlineNetLabelSolver extends BaseSolver {
|
|
|
1248
1250
|
queuedConnections: InlineEligibleConnection[];
|
|
1249
1251
|
private tracesByPinPairKey;
|
|
1250
1252
|
private hasAlignedPortOnlyStubs;
|
|
1253
|
+
private postProcessedOutput?;
|
|
1251
1254
|
constructor(input: InlineNetLabelSolverInput);
|
|
1252
1255
|
getConstructorParams(): [InlineNetLabelSolverInput];
|
|
1253
1256
|
_step(): void;
|
|
@@ -1277,6 +1280,7 @@ declare class InlineNetLabelSolver extends BaseSolver {
|
|
|
1277
1280
|
*/
|
|
1278
1281
|
private getSupersededNetLabelKeys;
|
|
1279
1282
|
private getOutputTraces;
|
|
1283
|
+
private buildPostProcessedOutput;
|
|
1280
1284
|
getOutput(): {
|
|
1281
1285
|
traces: SolvedTracePath[];
|
|
1282
1286
|
netLabelPlacements: NetLabelPlacement[];
|
package/dist/index.js
CHANGED
|
@@ -420,8 +420,48 @@ function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
|
|
|
420
420
|
return edges;
|
|
421
421
|
}
|
|
422
422
|
|
|
423
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
|
|
424
|
+
var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
|
|
425
|
+
var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
|
|
426
|
+
function getDimsForOrientation(params) {
|
|
427
|
+
const { orientation, netLabelWidth, netLabelHeight } = params;
|
|
428
|
+
const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
|
|
429
|
+
const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
|
|
430
|
+
if (orientation === "y+" || orientation === "y-") {
|
|
431
|
+
return {
|
|
432
|
+
// Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
|
|
433
|
+
width: horizHeight,
|
|
434
|
+
height: horizWidth
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
width: horizWidth,
|
|
439
|
+
height: horizHeight
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function getCenterFromAnchor(anchor, orientation, width, height) {
|
|
443
|
+
switch (orientation) {
|
|
444
|
+
case "x+":
|
|
445
|
+
return { x: anchor.x + width / 2, y: anchor.y };
|
|
446
|
+
case "x-":
|
|
447
|
+
return { x: anchor.x - width / 2, y: anchor.y };
|
|
448
|
+
case "y+":
|
|
449
|
+
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
450
|
+
case "y-":
|
|
451
|
+
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
function getRectBounds(center, w, h) {
|
|
455
|
+
return {
|
|
456
|
+
minX: center.x - w / 2,
|
|
457
|
+
minY: center.y - h / 2,
|
|
458
|
+
maxX: center.x + w / 2,
|
|
459
|
+
maxY: center.y + h / 2
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
423
463
|
// lib/solvers/MspConnectionPairSolver/isLabeledPeripheralConnection.ts
|
|
424
|
-
var
|
|
464
|
+
var getLabeledConnectionRouteReason = ({
|
|
425
465
|
inputProblem,
|
|
426
466
|
chipMap,
|
|
427
467
|
pins
|
|
@@ -430,12 +470,10 @@ var isLabeledPeripheralConnection = ({
|
|
|
430
470
|
const directConnection = inputProblem.directConnections.find(
|
|
431
471
|
(connection) => connection.pinIds.includes(firstPin.pinId) && connection.pinIds.includes(secondPin.pinId)
|
|
432
472
|
);
|
|
433
|
-
if (directConnection?.netLabelWidth === void 0) return
|
|
473
|
+
if (directConnection?.netLabelWidth === void 0) return null;
|
|
434
474
|
const firstChip = chipMap[firstPin.chipId];
|
|
435
475
|
const secondChip = chipMap[secondPin.chipId];
|
|
436
|
-
if (!firstChip || !secondChip) return
|
|
437
|
-
const hasSinglePinPeripheral = firstChip.pins.length === 1 || secondChip.pins.length === 1;
|
|
438
|
-
if (!hasSinglePinPeripheral) return false;
|
|
476
|
+
if (!firstChip || !secondChip) return null;
|
|
439
477
|
let firstFacingDirection = firstPin._facingDirection;
|
|
440
478
|
if (!firstFacingDirection) {
|
|
441
479
|
firstFacingDirection = getPinDirection(firstPin, firstChip);
|
|
@@ -444,8 +482,20 @@ var isLabeledPeripheralConnection = ({
|
|
|
444
482
|
if (!secondFacingDirection) {
|
|
445
483
|
secondFacingDirection = getPinDirection(secondPin, secondChip);
|
|
446
484
|
}
|
|
447
|
-
|
|
448
|
-
|
|
485
|
+
const hasOpposingHorizontalDirections = firstFacingDirection === "x-" && secondFacingDirection === "x+" || firstFacingDirection === "x+" && secondFacingDirection === "x-";
|
|
486
|
+
if (!hasOpposingHorizontalDirections) return null;
|
|
487
|
+
const hasSinglePinPeripheral = firstChip.pins.length === 1 || secondChip.pins.length === 1;
|
|
488
|
+
if (hasSinglePinPeripheral) return "single-pin-peripheral";
|
|
489
|
+
const sharedSectionId = firstChip.sectionId && firstChip.sectionId === secondChip.sectionId ? firstChip.sectionId : null;
|
|
490
|
+
if (!sharedSectionId) return null;
|
|
491
|
+
const firstIsLeft = firstPin.x <= secondPin.x;
|
|
492
|
+
const directionsPointTowardEachOther = firstIsLeft ? firstFacingDirection === "x+" && secondFacingDirection === "x-" : firstFacingDirection === "x-" && secondFacingDirection === "x+";
|
|
493
|
+
const labelsOverlapAlongX = Math.abs(firstPin.x - secondPin.x) < directConnection.netLabelWidth * 2;
|
|
494
|
+
const labelsOverlapAlongY = Math.abs(firstPin.y - secondPin.y) < NET_LABEL_HORIZONTAL_HEIGHT;
|
|
495
|
+
const fallbackLabelsOverlap = directionsPointTowardEachOther && labelsOverlapAlongX && labelsOverlapAlongY;
|
|
496
|
+
return fallbackLabelsOverlap ? "overlapping-fallback-labels" : null;
|
|
497
|
+
};
|
|
498
|
+
var isLabeledPeripheralConnection = (params) => getLabeledConnectionRouteReason(params) !== null;
|
|
449
499
|
|
|
450
500
|
// lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts
|
|
451
501
|
var DEFAULT_MAX_MSP_PAIR_DISTANCE = 1;
|
|
@@ -520,12 +570,12 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
520
570
|
if (this.directConnectionPinPairKeys.has(pinPairKey)) {
|
|
521
571
|
pairDistance = distance(p1, p2);
|
|
522
572
|
}
|
|
523
|
-
const
|
|
573
|
+
const labeledConnectionRouteReason = getLabeledConnectionRouteReason({
|
|
524
574
|
inputProblem: this.inputProblem,
|
|
525
575
|
chipMap: this.chipMap,
|
|
526
576
|
pins: [p1, p2]
|
|
527
577
|
});
|
|
528
|
-
if (pairDistance > this.maxMspPairDistance && !
|
|
578
|
+
if (pairDistance > this.maxMspPairDistance && !labeledConnectionRouteReason) {
|
|
529
579
|
return;
|
|
530
580
|
}
|
|
531
581
|
if (arePinsInDifferentSchematicSections(this.inputProblem, p1, p2)) {
|
|
@@ -543,11 +593,13 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
543
593
|
}
|
|
544
594
|
const globalConnNetId = this.globalConnMap.getNetConnectedToId(pin1);
|
|
545
595
|
const userNetId = this.userNetIdByPinId[pin1] ?? this.userNetIdByPinId[pin2];
|
|
596
|
+
const suppressNetLabel = pairDistance > this.maxMspPairDistance && labeledConnectionRouteReason === "overlapping-fallback-labels";
|
|
546
597
|
this.mspConnectionPairs.push({
|
|
547
598
|
mspPairId: `${pin1}-${pin2}`,
|
|
548
599
|
dcConnNetId: dcNetId,
|
|
549
600
|
globalConnNetId,
|
|
550
601
|
userNetId,
|
|
602
|
+
suppressNetLabel,
|
|
551
603
|
pins: [p1, p2]
|
|
552
604
|
});
|
|
553
605
|
return;
|
|
@@ -626,46 +678,6 @@ import "graphics-debug";
|
|
|
626
678
|
// lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
|
|
627
679
|
import { calculateElbow as calculateElbow2 } from "calculate-elbow";
|
|
628
680
|
|
|
629
|
-
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
|
|
630
|
-
var NET_LABEL_HORIZONTAL_WIDTH = 0.45;
|
|
631
|
-
var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
|
|
632
|
-
function getDimsForOrientation(params) {
|
|
633
|
-
const { orientation, netLabelWidth, netLabelHeight } = params;
|
|
634
|
-
const horizWidth = typeof netLabelWidth === "number" ? netLabelWidth : NET_LABEL_HORIZONTAL_WIDTH;
|
|
635
|
-
const horizHeight = typeof netLabelHeight === "number" ? netLabelHeight : NET_LABEL_HORIZONTAL_HEIGHT;
|
|
636
|
-
if (orientation === "y+" || orientation === "y-") {
|
|
637
|
-
return {
|
|
638
|
-
// Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
|
|
639
|
-
width: horizHeight,
|
|
640
|
-
height: horizWidth
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
return {
|
|
644
|
-
width: horizWidth,
|
|
645
|
-
height: horizHeight
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
function getCenterFromAnchor(anchor, orientation, width, height) {
|
|
649
|
-
switch (orientation) {
|
|
650
|
-
case "x+":
|
|
651
|
-
return { x: anchor.x + width / 2, y: anchor.y };
|
|
652
|
-
case "x-":
|
|
653
|
-
return { x: anchor.x - width / 2, y: anchor.y };
|
|
654
|
-
case "y+":
|
|
655
|
-
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
656
|
-
case "y-":
|
|
657
|
-
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
function getRectBounds(center, w, h) {
|
|
661
|
-
return {
|
|
662
|
-
minX: center.x - w / 2,
|
|
663
|
-
minY: center.y - h / 2,
|
|
664
|
-
maxX: center.x + w / 2,
|
|
665
|
-
maxY: center.y + h / 2
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
|
|
669
681
|
// lib/utils/textBoxBounds.ts
|
|
670
682
|
function getTextBoxBounds(textBox, padding = {}) {
|
|
671
683
|
return {
|
|
@@ -3148,6 +3160,7 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
3148
3160
|
(t) => component.has(t.pins[0].pinId) && component.has(t.pins[1].pinId)
|
|
3149
3161
|
);
|
|
3150
3162
|
if (compTraces.length > 0) {
|
|
3163
|
+
if (compTraces.some((trace) => trace.suppressNetLabel)) continue;
|
|
3151
3164
|
const lengthOf = (path) => {
|
|
3152
3165
|
let sum = 0;
|
|
3153
3166
|
const pts = path.tracePath;
|
|
@@ -10945,10 +10958,10 @@ function isPointOnPath(point, path) {
|
|
|
10945
10958
|
}
|
|
10946
10959
|
function buildMergedObstacleLabel(seedLabel, allLabels) {
|
|
10947
10960
|
const TOUCH_MARGIN = 0.01;
|
|
10948
|
-
const
|
|
10961
|
+
const getBounds4 = (l) => getRectBounds(l.center, l.width, l.height);
|
|
10949
10962
|
const touches = (a, b) => {
|
|
10950
|
-
const ba =
|
|
10951
|
-
const bb =
|
|
10963
|
+
const ba = getBounds4(a);
|
|
10964
|
+
const bb = getBounds4(b);
|
|
10952
10965
|
return ba.minX <= bb.maxX + TOUCH_MARGIN && ba.maxX >= bb.minX - TOUCH_MARGIN && ba.minY <= bb.maxY + TOUCH_MARGIN && ba.maxY >= bb.minY - TOUCH_MARGIN;
|
|
10953
10966
|
};
|
|
10954
10967
|
const group = /* @__PURE__ */ new Set([seedLabel]);
|
|
@@ -10965,7 +10978,7 @@ function buildMergedObstacleLabel(seedLabel, allLabels) {
|
|
|
10965
10978
|
}
|
|
10966
10979
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
10967
10980
|
for (const l of group) {
|
|
10968
|
-
const b =
|
|
10981
|
+
const b = getBounds4(l);
|
|
10969
10982
|
if (b.minX < minX) minX = b.minX;
|
|
10970
10983
|
if (b.minY < minY) minY = b.minY;
|
|
10971
10984
|
if (b.maxX > maxX) maxX = b.maxX;
|
|
@@ -12776,6 +12789,365 @@ var alignPortOnlyInlineNetLabelStubs = ({
|
|
|
12776
12789
|
return alignedPlacements;
|
|
12777
12790
|
};
|
|
12778
12791
|
|
|
12792
|
+
// lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts
|
|
12793
|
+
var LABEL_CLEARANCE2 = 0.05;
|
|
12794
|
+
var POINT_EPSILON = 1e-6;
|
|
12795
|
+
var CONTIGUOUS_LABEL_GAP = 0.01;
|
|
12796
|
+
var MAX_OUTWARD_DISTANCE = 5;
|
|
12797
|
+
var getBounds3 = (placement) => {
|
|
12798
|
+
const renderedWidth = placement.axis === "y" ? placement.height : placement.width;
|
|
12799
|
+
const renderedHeight = placement.axis === "y" ? placement.width : placement.height;
|
|
12800
|
+
return {
|
|
12801
|
+
minX: placement.center.x - renderedWidth / 2,
|
|
12802
|
+
maxX: placement.center.x + renderedWidth / 2,
|
|
12803
|
+
minY: placement.center.y - renderedHeight / 2,
|
|
12804
|
+
maxY: placement.center.y + renderedHeight / 2
|
|
12805
|
+
};
|
|
12806
|
+
};
|
|
12807
|
+
var pointsEqual3 = (a, b) => Math.abs(a.x - b.x) <= POINT_EPSILON && Math.abs(a.y - b.y) <= POINT_EPSILON;
|
|
12808
|
+
var pathIntersectsBounds = (path, bounds) => {
|
|
12809
|
+
for (let index = 0; index < path.length - 1; index++) {
|
|
12810
|
+
const start = path[index];
|
|
12811
|
+
const end = path[index + 1];
|
|
12812
|
+
const segmentBounds = {
|
|
12813
|
+
minX: Math.min(start.x, end.x),
|
|
12814
|
+
maxX: Math.max(start.x, end.x),
|
|
12815
|
+
minY: Math.min(start.y, end.y),
|
|
12816
|
+
maxY: Math.max(start.y, end.y)
|
|
12817
|
+
};
|
|
12818
|
+
if (boundsOverlap(segmentBounds, bounds)) return true;
|
|
12819
|
+
}
|
|
12820
|
+
return false;
|
|
12821
|
+
};
|
|
12822
|
+
var isPointOnPath2 = (point, path) => {
|
|
12823
|
+
for (let index = 0; index < path.length - 1; index++) {
|
|
12824
|
+
const start = path[index];
|
|
12825
|
+
const end = path[index + 1];
|
|
12826
|
+
const minX = Math.min(start.x, end.x) - POINT_EPSILON;
|
|
12827
|
+
const maxX = Math.max(start.x, end.x) + POINT_EPSILON;
|
|
12828
|
+
const minY = Math.min(start.y, end.y) - POINT_EPSILON;
|
|
12829
|
+
const maxY = Math.max(start.y, end.y) + POINT_EPSILON;
|
|
12830
|
+
const isHorizontal4 = Math.abs(start.y - end.y) <= POINT_EPSILON;
|
|
12831
|
+
const isVertical5 = Math.abs(start.x - end.x) <= POINT_EPSILON;
|
|
12832
|
+
if ((isHorizontal4 && Math.abs(point.y - start.y) <= POINT_EPSILON || isVertical5 && Math.abs(point.x - start.x) <= POINT_EPSILON) && point.x >= minX && point.x <= maxX && point.y >= minY && point.y <= maxY) {
|
|
12833
|
+
return true;
|
|
12834
|
+
}
|
|
12835
|
+
}
|
|
12836
|
+
return false;
|
|
12837
|
+
};
|
|
12838
|
+
var getRequiredOutwardDistance = (label, inlineBounds) => {
|
|
12839
|
+
const labelBounds = getBounds3(label);
|
|
12840
|
+
if (label.orientation === "x-" || label.orientation === "x+") {
|
|
12841
|
+
const nearby2 = inlineBounds.filter(
|
|
12842
|
+
(bounds) => labelBounds.minY < bounds.maxY && labelBounds.maxY > bounds.minY
|
|
12843
|
+
);
|
|
12844
|
+
if (nearby2.length === 0) return 0;
|
|
12845
|
+
if (label.orientation === "x-") {
|
|
12846
|
+
const targetMaxX = Math.min(...nearby2.map((bounds) => bounds.minX));
|
|
12847
|
+
return Math.max(0, labelBounds.maxX - targetMaxX + LABEL_CLEARANCE2);
|
|
12848
|
+
}
|
|
12849
|
+
const targetMinX = Math.max(...nearby2.map((bounds) => bounds.maxX));
|
|
12850
|
+
return Math.max(0, targetMinX - labelBounds.minX + LABEL_CLEARANCE2);
|
|
12851
|
+
}
|
|
12852
|
+
const nearby = inlineBounds.filter(
|
|
12853
|
+
(bounds) => labelBounds.minX < bounds.maxX && labelBounds.maxX > bounds.minX
|
|
12854
|
+
);
|
|
12855
|
+
if (nearby.length === 0) return 0;
|
|
12856
|
+
if (label.orientation === "y-") {
|
|
12857
|
+
const targetMaxY = Math.min(...nearby.map((bounds) => bounds.minY));
|
|
12858
|
+
return Math.max(0, labelBounds.maxY - targetMaxY + LABEL_CLEARANCE2);
|
|
12859
|
+
}
|
|
12860
|
+
const targetMinY = Math.max(...nearby.map((bounds) => bounds.maxY));
|
|
12861
|
+
return Math.max(0, targetMinY - labelBounds.minY + LABEL_CLEARANCE2);
|
|
12862
|
+
};
|
|
12863
|
+
var moveLabel = (label, orientation, distance5) => {
|
|
12864
|
+
const direction = dir(orientation);
|
|
12865
|
+
return {
|
|
12866
|
+
...label,
|
|
12867
|
+
anchorPoint: {
|
|
12868
|
+
x: label.anchorPoint.x + direction.x * distance5,
|
|
12869
|
+
y: label.anchorPoint.y + direction.y * distance5
|
|
12870
|
+
},
|
|
12871
|
+
center: {
|
|
12872
|
+
x: label.center.x + direction.x * distance5,
|
|
12873
|
+
y: label.center.y + direction.y * distance5
|
|
12874
|
+
}
|
|
12875
|
+
};
|
|
12876
|
+
};
|
|
12877
|
+
var getDistanceToShoveBoundsPast = (obstacleBounds, movingBounds, orientation) => {
|
|
12878
|
+
switch (orientation) {
|
|
12879
|
+
case "x-":
|
|
12880
|
+
return obstacleBounds.maxX - movingBounds.minX + LABEL_CLEARANCE2;
|
|
12881
|
+
case "x+":
|
|
12882
|
+
return movingBounds.maxX - obstacleBounds.minX + LABEL_CLEARANCE2;
|
|
12883
|
+
case "y-":
|
|
12884
|
+
return obstacleBounds.maxY - movingBounds.minY + LABEL_CLEARANCE2;
|
|
12885
|
+
case "y+":
|
|
12886
|
+
return movingBounds.maxY - obstacleBounds.minY + LABEL_CLEARANCE2;
|
|
12887
|
+
}
|
|
12888
|
+
};
|
|
12889
|
+
var boundsGapOnPerpendicularAxis = (a, b, orientation) => {
|
|
12890
|
+
if (orientation === "x-" || orientation === "x+") {
|
|
12891
|
+
return Math.max(0, a.minY - b.maxY, b.minY - a.maxY);
|
|
12892
|
+
}
|
|
12893
|
+
return Math.max(0, a.minX - b.maxX, b.minX - a.maxX);
|
|
12894
|
+
};
|
|
12895
|
+
var sharesOwnerChip = (label, ownerChipIds, chipIdByPinId) => label.pinIds.some((pinId) => ownerChipIds.has(chipIdByPinId.get(pinId) ?? ""));
|
|
12896
|
+
var isGeneratedLabelConnector = (trace) => trace.mspPairId.startsWith("available-net-orientation-") || trace.mspPairId.startsWith("inline-net-label-clearance-");
|
|
12897
|
+
var findConnectorTraceIndex = (label, traces) => traces.findIndex((trace) => {
|
|
12898
|
+
if (trace.globalConnNetId !== label.globalConnNetId) return false;
|
|
12899
|
+
if (!isGeneratedLabelConnector(trace)) return false;
|
|
12900
|
+
const first = trace.tracePath[0];
|
|
12901
|
+
const last = trace.tracePath.at(-1);
|
|
12902
|
+
return Boolean(
|
|
12903
|
+
first && pointsEqual3(first, label.anchorPoint) || last && pointsEqual3(last, label.anchorPoint)
|
|
12904
|
+
);
|
|
12905
|
+
});
|
|
12906
|
+
var canAddConnectorAtAnchor = (label, traces, pinMap) => {
|
|
12907
|
+
if (label.pinIds.some((pinId) => {
|
|
12908
|
+
const pin = pinMap[pinId];
|
|
12909
|
+
return pin && pointsEqual3(pin, label.anchorPoint);
|
|
12910
|
+
})) {
|
|
12911
|
+
return true;
|
|
12912
|
+
}
|
|
12913
|
+
return traces.some(
|
|
12914
|
+
(trace) => trace.globalConnNetId === label.globalConnNetId && isPointOnPath2(label.anchorPoint, trace.tracePath)
|
|
12915
|
+
);
|
|
12916
|
+
};
|
|
12917
|
+
var moveConnectorEndpoint = (trace, oldAnchor, newAnchor) => {
|
|
12918
|
+
const tracePath = trace.tracePath.map((point) => ({ ...point }));
|
|
12919
|
+
if (pointsEqual3(tracePath[0], oldAnchor)) tracePath[0] = newAnchor;
|
|
12920
|
+
if (pointsEqual3(tracePath.at(-1), oldAnchor)) {
|
|
12921
|
+
tracePath[tracePath.length - 1] = newAnchor;
|
|
12922
|
+
}
|
|
12923
|
+
return { ...trace, tracePath };
|
|
12924
|
+
};
|
|
12925
|
+
var createConnectorTrace = ({
|
|
12926
|
+
label,
|
|
12927
|
+
labelIndex,
|
|
12928
|
+
newAnchor,
|
|
12929
|
+
pinMap
|
|
12930
|
+
}) => {
|
|
12931
|
+
const mspPairId = `inline-net-label-clearance-${labelIndex}-${label.netId ?? label.globalConnNetId}`;
|
|
12932
|
+
return {
|
|
12933
|
+
mspPairId,
|
|
12934
|
+
dcConnNetId: label.dcConnNetId ?? label.globalConnNetId,
|
|
12935
|
+
globalConnNetId: label.globalConnNetId,
|
|
12936
|
+
userNetId: label.netId,
|
|
12937
|
+
pins: getTracePins(label, pinMap),
|
|
12938
|
+
tracePath: [label.anchorPoint, newAnchor],
|
|
12939
|
+
mspConnectionPairIds: [mspPairId],
|
|
12940
|
+
pinIds: label.pinIds
|
|
12941
|
+
};
|
|
12942
|
+
};
|
|
12943
|
+
var getContiguousLabelGroup = ({
|
|
12944
|
+
triggerIndex,
|
|
12945
|
+
labels,
|
|
12946
|
+
chipIdByPinId
|
|
12947
|
+
}) => {
|
|
12948
|
+
const trigger = labels[triggerIndex];
|
|
12949
|
+
const ownerChipIds = new Set(
|
|
12950
|
+
trigger.pinIds.flatMap((pinId) => {
|
|
12951
|
+
const chipId = chipIdByPinId.get(pinId);
|
|
12952
|
+
return chipId ? [chipId] : [];
|
|
12953
|
+
})
|
|
12954
|
+
);
|
|
12955
|
+
const candidates = labels.map((label, labelIndex) => ({ label, labelIndex })).filter(
|
|
12956
|
+
({ label }) => label.orientation === trigger.orientation && label.mspConnectionPairIds.length === 0 && sharesOwnerChip(label, ownerChipIds, chipIdByPinId)
|
|
12957
|
+
);
|
|
12958
|
+
const group = /* @__PURE__ */ new Set([triggerIndex]);
|
|
12959
|
+
let changed = true;
|
|
12960
|
+
while (changed) {
|
|
12961
|
+
changed = false;
|
|
12962
|
+
for (const { label, labelIndex } of candidates) {
|
|
12963
|
+
if (group.has(labelIndex)) continue;
|
|
12964
|
+
if ([...group].some(
|
|
12965
|
+
(memberIndex) => boundsGapOnPerpendicularAxis(
|
|
12966
|
+
getBounds3(labels[memberIndex]),
|
|
12967
|
+
getBounds3(label),
|
|
12968
|
+
trigger.orientation
|
|
12969
|
+
) <= CONTIGUOUS_LABEL_GAP
|
|
12970
|
+
)) {
|
|
12971
|
+
group.add(labelIndex);
|
|
12972
|
+
changed = true;
|
|
12973
|
+
}
|
|
12974
|
+
}
|
|
12975
|
+
}
|
|
12976
|
+
return { group, ownerChipIds };
|
|
12977
|
+
};
|
|
12978
|
+
var pushAnchoredNetLabelsAwayFromInlineLabels = ({
|
|
12979
|
+
inputProblem,
|
|
12980
|
+
traces,
|
|
12981
|
+
netLabelPlacements,
|
|
12982
|
+
inlineNetLabelPlacements
|
|
12983
|
+
}) => {
|
|
12984
|
+
const outputTraces = traces.map((trace) => ({
|
|
12985
|
+
...trace,
|
|
12986
|
+
tracePath: trace.tracePath.map((point) => ({ ...point }))
|
|
12987
|
+
}));
|
|
12988
|
+
const outputLabels = netLabelPlacements.map((label) => ({ ...label }));
|
|
12989
|
+
const inlineBounds = inlineNetLabelPlacements.map(getBounds3);
|
|
12990
|
+
const pinMap = getPinMap(inputProblem);
|
|
12991
|
+
const chipIdByPinId = /* @__PURE__ */ new Map();
|
|
12992
|
+
for (const chip of inputProblem.chips) {
|
|
12993
|
+
for (const pin of chip.pins) chipIdByPinId.set(pin.pinId, chip.chipId);
|
|
12994
|
+
}
|
|
12995
|
+
const movedLabelIndices = /* @__PURE__ */ new Set();
|
|
12996
|
+
for (let triggerIndex = 0; triggerIndex < outputLabels.length; triggerIndex++) {
|
|
12997
|
+
const trigger = outputLabels[triggerIndex];
|
|
12998
|
+
const distance5 = getRequiredOutwardDistance(trigger, inlineBounds);
|
|
12999
|
+
if (distance5 <= POINT_EPSILON || distance5 > MAX_OUTWARD_DISTANCE) continue;
|
|
13000
|
+
const { group, ownerChipIds } = getContiguousLabelGroup({
|
|
13001
|
+
triggerIndex,
|
|
13002
|
+
labels: outputLabels,
|
|
13003
|
+
chipIdByPinId
|
|
13004
|
+
});
|
|
13005
|
+
const distances = new Map(
|
|
13006
|
+
[...group].map((labelIndex) => [labelIndex, distance5])
|
|
13007
|
+
);
|
|
13008
|
+
let failed = false;
|
|
13009
|
+
for (let iteration = 0; iteration < outputLabels.length; iteration++) {
|
|
13010
|
+
let adjustedObstacle = false;
|
|
13011
|
+
for (const [movingIndex, movingDistance] of distances) {
|
|
13012
|
+
const movingBounds = getBounds3(
|
|
13013
|
+
moveLabel(
|
|
13014
|
+
outputLabels[movingIndex],
|
|
13015
|
+
trigger.orientation,
|
|
13016
|
+
movingDistance
|
|
13017
|
+
)
|
|
13018
|
+
);
|
|
13019
|
+
for (let obstacleIndex = 0; obstacleIndex < outputLabels.length; obstacleIndex++) {
|
|
13020
|
+
if (group.has(obstacleIndex)) continue;
|
|
13021
|
+
const obstacle = outputLabels[obstacleIndex];
|
|
13022
|
+
const existingObstacleDistance = distances.get(obstacleIndex) ?? 0;
|
|
13023
|
+
const obstacleBounds = getBounds3(
|
|
13024
|
+
moveLabel(obstacle, trigger.orientation, existingObstacleDistance)
|
|
13025
|
+
);
|
|
13026
|
+
if (!boundsOverlap(movingBounds, obstacleBounds)) continue;
|
|
13027
|
+
if (!sharesOwnerChip(obstacle, ownerChipIds, chipIdByPinId) || findConnectorTraceIndex(obstacle, outputTraces) === -1 && !canAddConnectorAtAnchor(obstacle, outputTraces, pinMap)) {
|
|
13028
|
+
failed = true;
|
|
13029
|
+
break;
|
|
13030
|
+
}
|
|
13031
|
+
const shoveDistance = getDistanceToShoveBoundsPast(
|
|
13032
|
+
getBounds3(obstacle),
|
|
13033
|
+
movingBounds,
|
|
13034
|
+
trigger.orientation
|
|
13035
|
+
);
|
|
13036
|
+
if (shoveDistance > MAX_OUTWARD_DISTANCE || shoveDistance <= existingObstacleDistance + POINT_EPSILON) {
|
|
13037
|
+
failed = true;
|
|
13038
|
+
break;
|
|
13039
|
+
}
|
|
13040
|
+
distances.set(obstacleIndex, shoveDistance);
|
|
13041
|
+
adjustedObstacle = true;
|
|
13042
|
+
}
|
|
13043
|
+
if (failed) break;
|
|
13044
|
+
}
|
|
13045
|
+
if (failed || !adjustedObstacle) break;
|
|
13046
|
+
}
|
|
13047
|
+
if (failed) continue;
|
|
13048
|
+
const proposals = /* @__PURE__ */ new Map();
|
|
13049
|
+
for (const [labelIndex, labelDistance] of distances) {
|
|
13050
|
+
proposals.set(
|
|
13051
|
+
labelIndex,
|
|
13052
|
+
moveLabel(
|
|
13053
|
+
outputLabels[labelIndex],
|
|
13054
|
+
trigger.orientation,
|
|
13055
|
+
labelDistance
|
|
13056
|
+
)
|
|
13057
|
+
);
|
|
13058
|
+
}
|
|
13059
|
+
const finalLabelAt = (labelIndex) => proposals.get(labelIndex) ?? outputLabels[labelIndex];
|
|
13060
|
+
for (const [labelIndex, movedLabel] of proposals) {
|
|
13061
|
+
const movedBounds = getBounds3(movedLabel);
|
|
13062
|
+
if (inlineBounds.some((bounds) => boundsOverlap(movedBounds, bounds))) {
|
|
13063
|
+
failed = true;
|
|
13064
|
+
break;
|
|
13065
|
+
}
|
|
13066
|
+
if (inputProblem.chips.some(
|
|
13067
|
+
(chip) => boundsOverlap(movedBounds, {
|
|
13068
|
+
minX: chip.center.x - chip.width / 2,
|
|
13069
|
+
maxX: chip.center.x + chip.width / 2,
|
|
13070
|
+
minY: chip.center.y - chip.height / 2,
|
|
13071
|
+
maxY: chip.center.y + chip.height / 2
|
|
13072
|
+
})
|
|
13073
|
+
) || (inputProblem.textBoxes ?? []).some(
|
|
13074
|
+
(textBox) => boundsOverlap(movedBounds, getTextBoxBounds(textBox))
|
|
13075
|
+
)) {
|
|
13076
|
+
failed = true;
|
|
13077
|
+
break;
|
|
13078
|
+
}
|
|
13079
|
+
if (outputLabels.some(
|
|
13080
|
+
(_, otherIndex) => otherIndex !== labelIndex && boundsOverlap(movedBounds, getBounds3(finalLabelAt(otherIndex)))
|
|
13081
|
+
)) {
|
|
13082
|
+
failed = true;
|
|
13083
|
+
break;
|
|
13084
|
+
}
|
|
13085
|
+
if (outputTraces.some(
|
|
13086
|
+
(trace) => trace.globalConnNetId !== movedLabel.globalConnNetId && pathIntersectsBounds(trace.tracePath, movedBounds)
|
|
13087
|
+
)) {
|
|
13088
|
+
failed = true;
|
|
13089
|
+
break;
|
|
13090
|
+
}
|
|
13091
|
+
}
|
|
13092
|
+
if (failed) continue;
|
|
13093
|
+
const connectorUpdates = [];
|
|
13094
|
+
for (const [labelIndex, movedLabel] of proposals) {
|
|
13095
|
+
const label = outputLabels[labelIndex];
|
|
13096
|
+
const connectorIndex = findConnectorTraceIndex(label, outputTraces);
|
|
13097
|
+
if (connectorIndex === -1 && !canAddConnectorAtAnchor(label, outputTraces, pinMap)) {
|
|
13098
|
+
failed = true;
|
|
13099
|
+
break;
|
|
13100
|
+
}
|
|
13101
|
+
const connector = connectorIndex === -1 ? createConnectorTrace({
|
|
13102
|
+
label,
|
|
13103
|
+
labelIndex,
|
|
13104
|
+
newAnchor: movedLabel.anchorPoint,
|
|
13105
|
+
pinMap
|
|
13106
|
+
}) : moveConnectorEndpoint(
|
|
13107
|
+
outputTraces[connectorIndex],
|
|
13108
|
+
label.anchorPoint,
|
|
13109
|
+
movedLabel.anchorPoint
|
|
13110
|
+
);
|
|
13111
|
+
const connectorObstructed = inlineBounds.some(
|
|
13112
|
+
(bounds) => pathIntersectsBounds(connector.tracePath, bounds)
|
|
13113
|
+
) || inputProblem.chips.some(
|
|
13114
|
+
(chip) => pathIntersectsBounds(connector.tracePath, {
|
|
13115
|
+
minX: chip.center.x - chip.width / 2,
|
|
13116
|
+
maxX: chip.center.x + chip.width / 2,
|
|
13117
|
+
minY: chip.center.y - chip.height / 2,
|
|
13118
|
+
maxY: chip.center.y + chip.height / 2
|
|
13119
|
+
})
|
|
13120
|
+
) || (inputProblem.textBoxes ?? []).some(
|
|
13121
|
+
(textBox) => pathIntersectsBounds(connector.tracePath, getTextBoxBounds(textBox))
|
|
13122
|
+
) || outputLabels.some(
|
|
13123
|
+
(_, otherIndex) => otherIndex !== labelIndex && pathIntersectsBounds(
|
|
13124
|
+
connector.tracePath,
|
|
13125
|
+
getBounds3(finalLabelAt(otherIndex))
|
|
13126
|
+
)
|
|
13127
|
+
);
|
|
13128
|
+
if (connectorObstructed) {
|
|
13129
|
+
failed = true;
|
|
13130
|
+
break;
|
|
13131
|
+
}
|
|
13132
|
+
connectorUpdates.push({ labelIndex, connectorIndex, trace: connector });
|
|
13133
|
+
}
|
|
13134
|
+
if (failed) continue;
|
|
13135
|
+
for (const [labelIndex, movedLabel] of proposals) {
|
|
13136
|
+
outputLabels[labelIndex] = movedLabel;
|
|
13137
|
+
movedLabelIndices.add(labelIndex);
|
|
13138
|
+
}
|
|
13139
|
+
for (const update of connectorUpdates) {
|
|
13140
|
+
if (update.connectorIndex === -1) outputTraces.push(update.trace);
|
|
13141
|
+
else outputTraces[update.connectorIndex] = update.trace;
|
|
13142
|
+
}
|
|
13143
|
+
}
|
|
13144
|
+
return {
|
|
13145
|
+
traces: outputTraces,
|
|
13146
|
+
netLabelPlacements: outputLabels,
|
|
13147
|
+
movedLabelCount: movedLabelIndices.size
|
|
13148
|
+
};
|
|
13149
|
+
};
|
|
13150
|
+
|
|
12779
13151
|
// lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
|
|
12780
13152
|
var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
|
|
12781
13153
|
var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
|
|
@@ -12790,6 +13162,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12790
13162
|
queuedConnections;
|
|
12791
13163
|
tracesByPinPairKey;
|
|
12792
13164
|
hasAlignedPortOnlyStubs = false;
|
|
13165
|
+
postProcessedOutput;
|
|
12793
13166
|
constructor(input) {
|
|
12794
13167
|
super();
|
|
12795
13168
|
this.inputProblem = input.inputProblem;
|
|
@@ -12850,6 +13223,10 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12850
13223
|
this.hasAlignedPortOnlyStubs = true;
|
|
12851
13224
|
return;
|
|
12852
13225
|
}
|
|
13226
|
+
if (!this.postProcessedOutput) {
|
|
13227
|
+
this.postProcessedOutput = this.buildPostProcessedOutput();
|
|
13228
|
+
return;
|
|
13229
|
+
}
|
|
12853
13230
|
this.solved = true;
|
|
12854
13231
|
this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length;
|
|
12855
13232
|
}
|
|
@@ -13096,19 +13473,29 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
13096
13473
|
(trace) => !(supersededNetLabelKeys.has(trace.globalConnNetId) && trace.mspPairId.startsWith("available-net-orientation-"))
|
|
13097
13474
|
);
|
|
13098
13475
|
}
|
|
13099
|
-
|
|
13476
|
+
buildPostProcessedOutput() {
|
|
13100
13477
|
const superseded = this.getSupersededNetLabelKeys();
|
|
13478
|
+
const retainedNetLabelPlacements = this.inputNetLabelPlacements.filter(
|
|
13479
|
+
(placement) => !superseded.has(placement.globalConnNetId)
|
|
13480
|
+
);
|
|
13481
|
+
const outputTraces = this.getOutputTraces(superseded);
|
|
13482
|
+
const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
|
|
13483
|
+
inputProblem: this.inputProblem,
|
|
13484
|
+
traces: outputTraces,
|
|
13485
|
+
netLabelPlacements: retainedNetLabelPlacements,
|
|
13486
|
+
inlineNetLabelPlacements: this.inlineNetLabelPlacements
|
|
13487
|
+
});
|
|
13488
|
+
this.stats.pushedAnchoredNetLabelCount = pushed.movedLabelCount;
|
|
13101
13489
|
return {
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
// actual net trace, but discard that now-orphaned label connector.
|
|
13105
|
-
traces: this.getOutputTraces(superseded),
|
|
13106
|
-
netLabelPlacements: this.inputNetLabelPlacements.filter(
|
|
13107
|
-
(placement) => !superseded.has(placement.globalConnNetId)
|
|
13108
|
-
),
|
|
13490
|
+
traces: pushed.traces,
|
|
13491
|
+
netLabelPlacements: pushed.netLabelPlacements,
|
|
13109
13492
|
inlineNetLabelPlacements: this.inlineNetLabelPlacements
|
|
13110
13493
|
};
|
|
13111
13494
|
}
|
|
13495
|
+
getOutput() {
|
|
13496
|
+
if (this.postProcessedOutput) return this.postProcessedOutput;
|
|
13497
|
+
return this.buildPostProcessedOutput();
|
|
13498
|
+
}
|
|
13112
13499
|
visualize() {
|
|
13113
13500
|
const graphics = visualizeInputProblem(this.inputProblem);
|
|
13114
13501
|
graphics.lines ??= [];
|