@tscircuit/schematic-trace-solver 0.0.145 → 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 +2 -0
- package/dist/index.js +386 -12
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +30 -8
- package/lib/solvers/InlineNetLabelSolver/pushAnchoredNetLabelsAwayFromInlineLabels.ts +530 -0
- package/package.json +1 -1
- package/tests/assets/inline-net-label-anchored-label-clearance.json +56 -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/dist/index.d.ts
CHANGED
|
@@ -1250,6 +1250,7 @@ declare class InlineNetLabelSolver extends BaseSolver {
|
|
|
1250
1250
|
queuedConnections: InlineEligibleConnection[];
|
|
1251
1251
|
private tracesByPinPairKey;
|
|
1252
1252
|
private hasAlignedPortOnlyStubs;
|
|
1253
|
+
private postProcessedOutput?;
|
|
1253
1254
|
constructor(input: InlineNetLabelSolverInput);
|
|
1254
1255
|
getConstructorParams(): [InlineNetLabelSolverInput];
|
|
1255
1256
|
_step(): void;
|
|
@@ -1279,6 +1280,7 @@ declare class InlineNetLabelSolver extends BaseSolver {
|
|
|
1279
1280
|
*/
|
|
1280
1281
|
private getSupersededNetLabelKeys;
|
|
1281
1282
|
private getOutputTraces;
|
|
1283
|
+
private buildPostProcessedOutput;
|
|
1282
1284
|
getOutput(): {
|
|
1283
1285
|
traces: SolvedTracePath[];
|
|
1284
1286
|
netLabelPlacements: NetLabelPlacement[];
|
package/dist/index.js
CHANGED
|
@@ -10958,10 +10958,10 @@ function isPointOnPath(point, path) {
|
|
|
10958
10958
|
}
|
|
10959
10959
|
function buildMergedObstacleLabel(seedLabel, allLabels) {
|
|
10960
10960
|
const TOUCH_MARGIN = 0.01;
|
|
10961
|
-
const
|
|
10961
|
+
const getBounds4 = (l) => getRectBounds(l.center, l.width, l.height);
|
|
10962
10962
|
const touches = (a, b) => {
|
|
10963
|
-
const ba =
|
|
10964
|
-
const bb =
|
|
10963
|
+
const ba = getBounds4(a);
|
|
10964
|
+
const bb = getBounds4(b);
|
|
10965
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;
|
|
10966
10966
|
};
|
|
10967
10967
|
const group = /* @__PURE__ */ new Set([seedLabel]);
|
|
@@ -10978,7 +10978,7 @@ function buildMergedObstacleLabel(seedLabel, allLabels) {
|
|
|
10978
10978
|
}
|
|
10979
10979
|
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
10980
10980
|
for (const l of group) {
|
|
10981
|
-
const b =
|
|
10981
|
+
const b = getBounds4(l);
|
|
10982
10982
|
if (b.minX < minX) minX = b.minX;
|
|
10983
10983
|
if (b.minY < minY) minY = b.minY;
|
|
10984
10984
|
if (b.maxX > maxX) maxX = b.maxX;
|
|
@@ -12789,6 +12789,365 @@ var alignPortOnlyInlineNetLabelStubs = ({
|
|
|
12789
12789
|
return alignedPlacements;
|
|
12790
12790
|
};
|
|
12791
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
|
+
|
|
12792
13151
|
// lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
|
|
12793
13152
|
var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
|
|
12794
13153
|
var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
|
|
@@ -12803,6 +13162,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12803
13162
|
queuedConnections;
|
|
12804
13163
|
tracesByPinPairKey;
|
|
12805
13164
|
hasAlignedPortOnlyStubs = false;
|
|
13165
|
+
postProcessedOutput;
|
|
12806
13166
|
constructor(input) {
|
|
12807
13167
|
super();
|
|
12808
13168
|
this.inputProblem = input.inputProblem;
|
|
@@ -12863,6 +13223,10 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12863
13223
|
this.hasAlignedPortOnlyStubs = true;
|
|
12864
13224
|
return;
|
|
12865
13225
|
}
|
|
13226
|
+
if (!this.postProcessedOutput) {
|
|
13227
|
+
this.postProcessedOutput = this.buildPostProcessedOutput();
|
|
13228
|
+
return;
|
|
13229
|
+
}
|
|
12866
13230
|
this.solved = true;
|
|
12867
13231
|
this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length;
|
|
12868
13232
|
}
|
|
@@ -13109,19 +13473,29 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
13109
13473
|
(trace) => !(supersededNetLabelKeys.has(trace.globalConnNetId) && trace.mspPairId.startsWith("available-net-orientation-"))
|
|
13110
13474
|
);
|
|
13111
13475
|
}
|
|
13112
|
-
|
|
13476
|
+
buildPostProcessedOutput() {
|
|
13113
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;
|
|
13114
13489
|
return {
|
|
13115
|
-
|
|
13116
|
-
|
|
13117
|
-
// actual net trace, but discard that now-orphaned label connector.
|
|
13118
|
-
traces: this.getOutputTraces(superseded),
|
|
13119
|
-
netLabelPlacements: this.inputNetLabelPlacements.filter(
|
|
13120
|
-
(placement) => !superseded.has(placement.globalConnNetId)
|
|
13121
|
-
),
|
|
13490
|
+
traces: pushed.traces,
|
|
13491
|
+
netLabelPlacements: pushed.netLabelPlacements,
|
|
13122
13492
|
inlineNetLabelPlacements: this.inlineNetLabelPlacements
|
|
13123
13493
|
};
|
|
13124
13494
|
}
|
|
13495
|
+
getOutput() {
|
|
13496
|
+
if (this.postProcessedOutput) return this.postProcessedOutput;
|
|
13497
|
+
return this.buildPostProcessedOutput();
|
|
13498
|
+
}
|
|
13125
13499
|
visualize() {
|
|
13126
13500
|
const graphics = visualizeInputProblem(this.inputProblem);
|
|
13127
13501
|
graphics.lines ??= [];
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
getAxisAlignedSegments,
|
|
19
19
|
} from "./getAxisAlignedSegments"
|
|
20
20
|
import { alignPortOnlyInlineNetLabelStubs } from "./alignPortOnlyInlineNetLabelStubs"
|
|
21
|
+
import { pushAnchoredNetLabelsAwayFromInlineLabels } from "./pushAnchoredNetLabelsAwayFromInlineLabels"
|
|
21
22
|
|
|
22
23
|
export const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18
|
|
23
24
|
|
|
@@ -111,6 +112,11 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
111
112
|
|
|
112
113
|
private tracesByPinPairKey: Map<string, SolvedTracePath[]>
|
|
113
114
|
private hasAlignedPortOnlyStubs = false
|
|
115
|
+
private postProcessedOutput?: {
|
|
116
|
+
traces: SolvedTracePath[]
|
|
117
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
118
|
+
inlineNetLabelPlacements: InlineNetLabelPlacement[]
|
|
119
|
+
}
|
|
114
120
|
|
|
115
121
|
constructor(input: InlineNetLabelSolverInput) {
|
|
116
122
|
super()
|
|
@@ -191,6 +197,11 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
191
197
|
return
|
|
192
198
|
}
|
|
193
199
|
|
|
200
|
+
if (!this.postProcessedOutput) {
|
|
201
|
+
this.postProcessedOutput = this.buildPostProcessedOutput()
|
|
202
|
+
return
|
|
203
|
+
}
|
|
204
|
+
|
|
194
205
|
this.solved = true
|
|
195
206
|
this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length
|
|
196
207
|
}
|
|
@@ -584,20 +595,31 @@ export class InlineNetLabelSolver extends BaseSolver {
|
|
|
584
595
|
)
|
|
585
596
|
}
|
|
586
597
|
|
|
587
|
-
|
|
598
|
+
private buildPostProcessedOutput() {
|
|
588
599
|
const superseded = this.getSupersededNetLabelKeys()
|
|
600
|
+
const retainedNetLabelPlacements = this.inputNetLabelPlacements.filter(
|
|
601
|
+
(placement) => !superseded.has(placement.globalConnNetId),
|
|
602
|
+
)
|
|
603
|
+
const outputTraces = this.getOutputTraces(superseded)
|
|
604
|
+
const pushed = pushAnchoredNetLabelsAwayFromInlineLabels({
|
|
605
|
+
inputProblem: this.inputProblem,
|
|
606
|
+
traces: outputTraces,
|
|
607
|
+
netLabelPlacements: retainedNetLabelPlacements,
|
|
608
|
+
inlineNetLabelPlacements: this.inlineNetLabelPlacements,
|
|
609
|
+
})
|
|
610
|
+
this.stats.pushedAnchoredNetLabelCount = pushed.movedLabelCount
|
|
589
611
|
return {
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
// actual net trace, but discard that now-orphaned label connector.
|
|
593
|
-
traces: this.getOutputTraces(superseded),
|
|
594
|
-
netLabelPlacements: this.inputNetLabelPlacements.filter(
|
|
595
|
-
(placement) => !superseded.has(placement.globalConnNetId),
|
|
596
|
-
),
|
|
612
|
+
traces: pushed.traces,
|
|
613
|
+
netLabelPlacements: pushed.netLabelPlacements,
|
|
597
614
|
inlineNetLabelPlacements: this.inlineNetLabelPlacements,
|
|
598
615
|
}
|
|
599
616
|
}
|
|
600
617
|
|
|
618
|
+
getOutput() {
|
|
619
|
+
if (this.postProcessedOutput) return this.postProcessedOutput
|
|
620
|
+
return this.buildPostProcessedOutput()
|
|
621
|
+
}
|
|
622
|
+
|
|
601
623
|
override visualize(): GraphicsObject {
|
|
602
624
|
// Mirrors the previous pipeline stage's visualization so that a problem
|
|
603
625
|
// with no inline labels renders identically, then layers the inline labels
|