@tscircuit/schematic-trace-solver 0.0.141 → 0.0.142
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 +27 -1
- package/dist/index.js +280 -18
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +216 -17
- package/lib/solvers/InlineNetLabelSolver/alignPortOnlyInlineNetLabelStubs.ts +233 -0
- package/lib/types/InputProblem.ts +13 -0
- package/package.json +1 -1
- package/tests/assets/inline-net-label-port-stub.json +67 -0
- package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +52 -0
- package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +624 -0
- package/tests/repros/assets/repro-tida010076-page02.input.json +2964 -0
- package/tests/repros/repro-tida010076-j4-ground-bus.test.ts +78 -0
- package/tests/repros/repro-tida010076-page02.test.ts +27 -0
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-port-stub.snap.svg +64 -0
- package/tests/solvers/InlineNetLabelSolver/align-port-only-inline-net-label-stubs-conflict.test.ts +75 -0
- package/tests/solvers/InlineNetLabelSolver/inline-net-label-port-stub.test.ts +82 -0
package/dist/index.d.ts
CHANGED
|
@@ -115,6 +115,16 @@ interface InputNetConnection {
|
|
|
115
115
|
pinIds: Array<PinId>;
|
|
116
116
|
netLabelWidth?: number;
|
|
117
117
|
netLabelHeight?: number;
|
|
118
|
+
/**
|
|
119
|
+
* When true, a named single-pin net may be drawn as a short outward trace
|
|
120
|
+
* stub with its net name placed inline. Multi-pin net connections retain the
|
|
121
|
+
* regular anchored-label behavior.
|
|
122
|
+
*/
|
|
123
|
+
allowInlineNetLabel?: boolean;
|
|
124
|
+
/** Extent of the inline text along the generated trace stub. */
|
|
125
|
+
inlineNetLabelWidth?: number;
|
|
126
|
+
/** Height of the inline text perpendicular to the generated trace stub. */
|
|
127
|
+
inlineNetLabelHeight?: number;
|
|
118
128
|
}
|
|
119
129
|
interface InputProblem {
|
|
120
130
|
chips: Array<InputChip>;
|
|
@@ -1186,8 +1196,13 @@ declare const INLINE_NET_LABEL_MAX_SPAN_JOG = 0.4;
|
|
|
1186
1196
|
interface InlineNetLabelPlacement {
|
|
1187
1197
|
globalConnNetId: string;
|
|
1188
1198
|
netId?: string;
|
|
1189
|
-
mspPairId
|
|
1199
|
+
mspPairId?: string;
|
|
1190
1200
|
pinIds: PinId[];
|
|
1201
|
+
/**
|
|
1202
|
+
* A generated single-ended trace stub. Present only for an eligible
|
|
1203
|
+
* single-pin net connection; routed point-to-point traces omit it.
|
|
1204
|
+
*/
|
|
1205
|
+
stubTracePath?: [Point, Point];
|
|
1191
1206
|
/** Axis the text runs along: "x" reads left-to-right, "y" reads bottom-to-top */
|
|
1192
1207
|
axis: "x" | "y";
|
|
1193
1208
|
/** Midpoint of the trace segment the label is attached to */
|
|
@@ -1224,10 +1239,20 @@ declare class InlineNetLabelSolver extends BaseSolver {
|
|
|
1224
1239
|
inlineNetLabelPlacements: InlineNetLabelPlacement[];
|
|
1225
1240
|
/** Direct connections that opted in, still waiting to be processed */
|
|
1226
1241
|
queuedDirectConnections: InputDirectConnection[];
|
|
1242
|
+
/** Single-pin net connections that opted in, still waiting to be processed */
|
|
1243
|
+
queuedPortOnlyNetConnections: InputNetConnection[];
|
|
1227
1244
|
private tracesByPinPairKey;
|
|
1245
|
+
private hasAlignedPortOnlyStubs;
|
|
1228
1246
|
constructor(input: InlineNetLabelSolverInput);
|
|
1229
1247
|
getConstructorParams(): [InlineNetLabelSolverInput];
|
|
1230
1248
|
_step(): void;
|
|
1249
|
+
/**
|
|
1250
|
+
* Converts a conventional port-only anchored placement into an inline label
|
|
1251
|
+
* on a generated outward stub. The stub follows the pin's true facing
|
|
1252
|
+
* direction; an anchored label may finish in another direction after an
|
|
1253
|
+
* elbow, which is not the direction a terminal stub should leave the pin.
|
|
1254
|
+
*/
|
|
1255
|
+
private computePortOnlyInlinePlacement;
|
|
1231
1256
|
private computeInlinePlacement;
|
|
1232
1257
|
/**
|
|
1233
1258
|
* Places the label over the whole route when the route is straight except
|
|
@@ -1246,6 +1271,7 @@ declare class InlineNetLabelSolver extends BaseSolver {
|
|
|
1246
1271
|
* the other, never both.
|
|
1247
1272
|
*/
|
|
1248
1273
|
private getSupersededNetLabelKeys;
|
|
1274
|
+
private getOutputTraces;
|
|
1249
1275
|
getOutput(): {
|
|
1250
1276
|
traces: SolvedTracePath[];
|
|
1251
1277
|
netLabelPlacements: NetLabelPlacement[];
|
package/dist/index.js
CHANGED
|
@@ -12576,6 +12576,154 @@ var getAxisAlignedSegments = (path, epsilon = 1e-6) => {
|
|
|
12576
12576
|
return segments.sort((a, b) => b.length - a.length);
|
|
12577
12577
|
};
|
|
12578
12578
|
|
|
12579
|
+
// lib/solvers/InlineNetLabelSolver/alignPortOnlyInlineNetLabelStubs.ts
|
|
12580
|
+
var getStubDirection = (path) => {
|
|
12581
|
+
const [start, end] = path;
|
|
12582
|
+
if (Math.abs(end.x - start.x) >= Math.abs(end.y - start.y)) {
|
|
12583
|
+
return end.x >= start.x ? "x+" : "x-";
|
|
12584
|
+
}
|
|
12585
|
+
return end.y >= start.y ? "y+" : "y-";
|
|
12586
|
+
};
|
|
12587
|
+
var getStubLength = (path) => {
|
|
12588
|
+
const [start, end] = path;
|
|
12589
|
+
return Math.abs(end.x - start.x) + Math.abs(end.y - start.y);
|
|
12590
|
+
};
|
|
12591
|
+
var getLabelBounds2 = (placement) => {
|
|
12592
|
+
const isVertical5 = placement.axis === "y";
|
|
12593
|
+
const width = isVertical5 ? placement.height : placement.width;
|
|
12594
|
+
const height = isVertical5 ? placement.width : placement.height;
|
|
12595
|
+
return {
|
|
12596
|
+
minX: placement.center.x - width / 2,
|
|
12597
|
+
maxX: placement.center.x + width / 2,
|
|
12598
|
+
minY: placement.center.y - height / 2,
|
|
12599
|
+
maxY: placement.center.y + height / 2
|
|
12600
|
+
};
|
|
12601
|
+
};
|
|
12602
|
+
var getPathBounds = (path) => ({
|
|
12603
|
+
minX: Math.min(...path.map((point) => point.x)),
|
|
12604
|
+
maxX: Math.max(...path.map((point) => point.x)),
|
|
12605
|
+
minY: Math.min(...path.map((point) => point.y)),
|
|
12606
|
+
maxY: Math.max(...path.map((point) => point.y))
|
|
12607
|
+
});
|
|
12608
|
+
var doesPathIntersectBounds = (path, bounds) => {
|
|
12609
|
+
for (let index = 0; index < path.length - 1; index++) {
|
|
12610
|
+
const segmentBounds = getPathBounds([path[index], path[index + 1]]);
|
|
12611
|
+
if (boundsOverlap(segmentBounds, bounds)) return true;
|
|
12612
|
+
}
|
|
12613
|
+
return false;
|
|
12614
|
+
};
|
|
12615
|
+
var resizeStub = (placement, direction, targetLength) => {
|
|
12616
|
+
const [start] = placement.stubTracePath;
|
|
12617
|
+
const end = direction === "x+" ? { x: start.x + targetLength, y: start.y } : direction === "x-" ? { x: start.x - targetLength, y: start.y } : direction === "y+" ? { x: start.x, y: start.y + targetLength } : { x: start.x, y: start.y - targetLength };
|
|
12618
|
+
const anchorPoint = {
|
|
12619
|
+
x: (start.x + end.x) / 2,
|
|
12620
|
+
y: (start.y + end.y) / 2
|
|
12621
|
+
};
|
|
12622
|
+
return {
|
|
12623
|
+
...placement,
|
|
12624
|
+
stubTracePath: [start, end],
|
|
12625
|
+
anchorPoint,
|
|
12626
|
+
// Extending a short row only adds wire at its free end. Keep the label
|
|
12627
|
+
// itself next to the pin so terminal labels can be start/end aligned and
|
|
12628
|
+
// collision checks continue to describe the rendered text box.
|
|
12629
|
+
center: placement.center
|
|
12630
|
+
};
|
|
12631
|
+
};
|
|
12632
|
+
var alignPortOnlyInlineNetLabelStubs = ({
|
|
12633
|
+
placements,
|
|
12634
|
+
inputProblem,
|
|
12635
|
+
traces,
|
|
12636
|
+
netLabelPlacements
|
|
12637
|
+
}) => {
|
|
12638
|
+
const chipIdByPinId = /* @__PURE__ */ new Map();
|
|
12639
|
+
for (const chip of inputProblem.chips) {
|
|
12640
|
+
for (const pin of chip.pins) chipIdByPinId.set(pin.pinId, chip.chipId);
|
|
12641
|
+
}
|
|
12642
|
+
const groups = /* @__PURE__ */ new Map();
|
|
12643
|
+
for (const [placementIndex, placement] of placements.entries()) {
|
|
12644
|
+
if (!placement.stubTracePath || placement.pinIds.length !== 1) continue;
|
|
12645
|
+
const direction = getStubDirection(placement.stubTracePath);
|
|
12646
|
+
if (!direction) continue;
|
|
12647
|
+
const ownerChipId = chipIdByPinId.get(placement.pinIds[0]);
|
|
12648
|
+
const groupKey = `${ownerChipId ?? placement.pinIds[0]}::${direction}`;
|
|
12649
|
+
const group = groups.get(groupKey) ?? [];
|
|
12650
|
+
group.push({ placementIndex, placement, direction, ownerChipId });
|
|
12651
|
+
groups.set(groupKey, group);
|
|
12652
|
+
}
|
|
12653
|
+
const alignedPlacements = [...placements];
|
|
12654
|
+
const supersededGlobalNetIds = new Set(
|
|
12655
|
+
placements.map((placement) => placement.globalConnNetId)
|
|
12656
|
+
);
|
|
12657
|
+
const retainedAnchoredLabelBounds = netLabelPlacements.filter(
|
|
12658
|
+
(placement) => !supersededGlobalNetIds.has(placement.globalConnNetId)
|
|
12659
|
+
).map(getLabelBounds2);
|
|
12660
|
+
for (const group of groups.values()) {
|
|
12661
|
+
if (group.length < 2) continue;
|
|
12662
|
+
const targetLength = Math.max(
|
|
12663
|
+
...group.map(({ placement }) => getStubLength(placement.stubTracePath))
|
|
12664
|
+
);
|
|
12665
|
+
const proposals = group.map(
|
|
12666
|
+
({ placement, direction }) => resizeStub(placement, direction, targetLength)
|
|
12667
|
+
);
|
|
12668
|
+
const groupPlacementIndices = new Set(
|
|
12669
|
+
group.map(({ placementIndex }) => placementIndex)
|
|
12670
|
+
);
|
|
12671
|
+
const fixedInlinePlacements = placements.filter(
|
|
12672
|
+
(_, placementIndex) => !groupPlacementIndices.has(placementIndex)
|
|
12673
|
+
);
|
|
12674
|
+
const hasConflict = proposals.some((proposal, proposalIndex) => {
|
|
12675
|
+
const labelBounds = getLabelBounds2(proposal);
|
|
12676
|
+
const stubPath = proposal.stubTracePath;
|
|
12677
|
+
const stubBounds = getPathBounds(stubPath);
|
|
12678
|
+
const ownerChipId = group[proposalIndex].ownerChipId;
|
|
12679
|
+
for (const chip of inputProblem.chips) {
|
|
12680
|
+
const chipBounds = {
|
|
12681
|
+
minX: chip.center.x - chip.width / 2,
|
|
12682
|
+
maxX: chip.center.x + chip.width / 2,
|
|
12683
|
+
minY: chip.center.y - chip.height / 2,
|
|
12684
|
+
maxY: chip.center.y + chip.height / 2
|
|
12685
|
+
};
|
|
12686
|
+
if (boundsOverlap(labelBounds, chipBounds)) return true;
|
|
12687
|
+
if (chip.chipId !== ownerChipId && boundsOverlap(stubBounds, chipBounds))
|
|
12688
|
+
return true;
|
|
12689
|
+
}
|
|
12690
|
+
for (const textBox of inputProblem.textBoxes ?? []) {
|
|
12691
|
+
const textBounds = getTextBoxBounds(textBox);
|
|
12692
|
+
if (boundsOverlap(labelBounds, textBounds) || boundsOverlap(stubBounds, textBounds))
|
|
12693
|
+
return true;
|
|
12694
|
+
}
|
|
12695
|
+
for (const trace of traces) {
|
|
12696
|
+
if (trace.globalConnNetId === proposal.globalConnNetId) continue;
|
|
12697
|
+
if (doesPathIntersectBounds(trace.tracePath, labelBounds) || doesPathIntersectBounds(trace.tracePath, stubBounds))
|
|
12698
|
+
return true;
|
|
12699
|
+
}
|
|
12700
|
+
if (retainedAnchoredLabelBounds.some(
|
|
12701
|
+
(bounds) => boundsOverlap(labelBounds, bounds) || boundsOverlap(stubBounds, bounds)
|
|
12702
|
+
))
|
|
12703
|
+
return true;
|
|
12704
|
+
for (const fixedPlacement of fixedInlinePlacements) {
|
|
12705
|
+
const fixedLabelBounds = getLabelBounds2(fixedPlacement);
|
|
12706
|
+
if (boundsOverlap(labelBounds, fixedLabelBounds) || boundsOverlap(stubBounds, fixedLabelBounds))
|
|
12707
|
+
return true;
|
|
12708
|
+
if (fixedPlacement.stubTracePath && (doesPathIntersectBounds(fixedPlacement.stubTracePath, labelBounds) || doesPathIntersectBounds(stubPath, fixedLabelBounds)))
|
|
12709
|
+
return true;
|
|
12710
|
+
}
|
|
12711
|
+
for (const [otherIndex, otherProposal] of proposals.entries()) {
|
|
12712
|
+
if (otherIndex === proposalIndex) continue;
|
|
12713
|
+
const otherLabelBounds = getLabelBounds2(otherProposal);
|
|
12714
|
+
if (boundsOverlap(labelBounds, otherLabelBounds)) return true;
|
|
12715
|
+
if (doesPathIntersectBounds(stubPath, otherLabelBounds)) return true;
|
|
12716
|
+
}
|
|
12717
|
+
return false;
|
|
12718
|
+
});
|
|
12719
|
+
if (hasConflict) continue;
|
|
12720
|
+
for (const [groupIndex, { placementIndex }] of group.entries()) {
|
|
12721
|
+
alignedPlacements[placementIndex] = proposals[groupIndex];
|
|
12722
|
+
}
|
|
12723
|
+
}
|
|
12724
|
+
return alignedPlacements;
|
|
12725
|
+
};
|
|
12726
|
+
|
|
12579
12727
|
// lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
|
|
12580
12728
|
var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
|
|
12581
12729
|
var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
|
|
@@ -12588,7 +12736,10 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12588
12736
|
inlineNetLabelPlacements = [];
|
|
12589
12737
|
/** Direct connections that opted in, still waiting to be processed */
|
|
12590
12738
|
queuedDirectConnections;
|
|
12739
|
+
/** Single-pin net connections that opted in, still waiting to be processed */
|
|
12740
|
+
queuedPortOnlyNetConnections;
|
|
12591
12741
|
tracesByPinPairKey;
|
|
12742
|
+
hasAlignedPortOnlyStubs = false;
|
|
12592
12743
|
constructor(input) {
|
|
12593
12744
|
super();
|
|
12594
12745
|
this.inputProblem = input.inputProblem;
|
|
@@ -12597,6 +12748,9 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12597
12748
|
this.queuedDirectConnections = this.inputProblem.directConnections.filter(
|
|
12598
12749
|
(dc) => dc.allowInlineNetLabel && dc.netId
|
|
12599
12750
|
);
|
|
12751
|
+
this.queuedPortOnlyNetConnections = this.inputProblem.netConnections.filter(
|
|
12752
|
+
(nc) => nc.allowInlineNetLabel && nc.pinIds.length === 1 && nc.netId
|
|
12753
|
+
);
|
|
12600
12754
|
this.tracesByPinPairKey = /* @__PURE__ */ new Map();
|
|
12601
12755
|
for (const trace of this.traces) {
|
|
12602
12756
|
const key = getPinPairKey2(trace.pins.map((p) => p.pinId));
|
|
@@ -12619,15 +12773,97 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12619
12773
|
}
|
|
12620
12774
|
_step() {
|
|
12621
12775
|
const directConnection = this.queuedDirectConnections.shift();
|
|
12622
|
-
if (
|
|
12623
|
-
|
|
12624
|
-
|
|
12776
|
+
if (directConnection) {
|
|
12777
|
+
const placement = this.computeInlinePlacement(directConnection);
|
|
12778
|
+
if (placement) {
|
|
12779
|
+
this.inlineNetLabelPlacements.push(placement);
|
|
12780
|
+
}
|
|
12781
|
+
return;
|
|
12782
|
+
}
|
|
12783
|
+
const portOnlyNetConnection = this.queuedPortOnlyNetConnections.shift();
|
|
12784
|
+
if (portOnlyNetConnection) {
|
|
12785
|
+
const placement = this.computePortOnlyInlinePlacement(
|
|
12786
|
+
portOnlyNetConnection
|
|
12787
|
+
);
|
|
12788
|
+
if (placement) {
|
|
12789
|
+
this.inlineNetLabelPlacements.push(placement);
|
|
12790
|
+
}
|
|
12625
12791
|
return;
|
|
12626
12792
|
}
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12793
|
+
if (!this.hasAlignedPortOnlyStubs) {
|
|
12794
|
+
this.inlineNetLabelPlacements = alignPortOnlyInlineNetLabelStubs({
|
|
12795
|
+
placements: this.inlineNetLabelPlacements,
|
|
12796
|
+
inputProblem: this.inputProblem,
|
|
12797
|
+
traces: this.traces,
|
|
12798
|
+
netLabelPlacements: this.inputNetLabelPlacements
|
|
12799
|
+
});
|
|
12800
|
+
this.hasAlignedPortOnlyStubs = true;
|
|
12801
|
+
return;
|
|
12802
|
+
}
|
|
12803
|
+
this.solved = true;
|
|
12804
|
+
this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length;
|
|
12805
|
+
}
|
|
12806
|
+
/**
|
|
12807
|
+
* Converts a conventional port-only anchored placement into an inline label
|
|
12808
|
+
* on a generated outward stub. The stub follows the pin's true facing
|
|
12809
|
+
* direction; an anchored label may finish in another direction after an
|
|
12810
|
+
* elbow, which is not the direction a terminal stub should leave the pin.
|
|
12811
|
+
*/
|
|
12812
|
+
computePortOnlyInlinePlacement(netConnection) {
|
|
12813
|
+
const [pinId] = netConnection.pinIds;
|
|
12814
|
+
if (!pinId) return null;
|
|
12815
|
+
const anchoredPlacement = this.inputNetLabelPlacements.find(
|
|
12816
|
+
(placement) => placement.netId === netConnection.netId && placement.pinIds.length === 1 && placement.pinIds[0] === pinId
|
|
12817
|
+
);
|
|
12818
|
+
if (!anchoredPlacement) return null;
|
|
12819
|
+
const inputChip = this.inputProblem.chips.find(
|
|
12820
|
+
(chip) => chip.pins.some((pin) => pin.pinId === pinId)
|
|
12821
|
+
);
|
|
12822
|
+
const inputPin = inputChip?.pins.find((pin) => pin.pinId === pinId);
|
|
12823
|
+
const height = netConnection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT;
|
|
12824
|
+
const width = netConnection.inlineNetLabelWidth ?? netConnection.netLabelWidth ?? estimateInlineNetLabelWidth(netConnection.netId, height);
|
|
12825
|
+
const stubLength = Math.max(width + 0.2, 0.6);
|
|
12826
|
+
const start = inputPin ? { x: inputPin.x, y: inputPin.y } : anchoredPlacement.anchorPoint;
|
|
12827
|
+
const direction = inputPin && inputChip ? inputPin._facingDirection ?? getPinDirection(inputPin, inputChip) : anchoredPlacement.orientation;
|
|
12828
|
+
const end = direction === "x+" ? { x: start.x + stubLength, y: start.y } : direction === "x-" ? { x: start.x - stubLength, y: start.y } : direction === "y+" ? { x: start.x, y: start.y + stubLength } : { x: start.x, y: start.y - stubLength };
|
|
12829
|
+
const axis = direction === "x+" || direction === "x-" ? "x" : "y";
|
|
12830
|
+
const side = axis === "x" ? "y+" : "x-";
|
|
12831
|
+
const anchorPoint = {
|
|
12832
|
+
x: (start.x + end.x) / 2,
|
|
12833
|
+
y: (start.y + end.y) / 2
|
|
12834
|
+
};
|
|
12835
|
+
const offset = height / 2 + INLINE_NET_LABEL_TRACE_MARGIN;
|
|
12836
|
+
const center = side === "y+" ? { x: anchorPoint.x, y: anchorPoint.y + offset } : { x: anchorPoint.x - offset, y: anchorPoint.y };
|
|
12837
|
+
const halfAlong = width / 2;
|
|
12838
|
+
const halfAcross = height / 2;
|
|
12839
|
+
const bounds = axis === "x" ? {
|
|
12840
|
+
minX: center.x - halfAlong,
|
|
12841
|
+
maxX: center.x + halfAlong,
|
|
12842
|
+
minY: center.y - halfAcross,
|
|
12843
|
+
maxY: center.y + halfAcross
|
|
12844
|
+
} : {
|
|
12845
|
+
minX: center.x - halfAcross,
|
|
12846
|
+
maxX: center.x + halfAcross,
|
|
12847
|
+
minY: center.y - halfAlong,
|
|
12848
|
+
maxY: center.y + halfAlong
|
|
12849
|
+
};
|
|
12850
|
+
if (this.isObstructed(bounds, {
|
|
12851
|
+
ownGlobalConnNetId: anchoredPlacement.globalConnNetId
|
|
12852
|
+
})) {
|
|
12853
|
+
return null;
|
|
12630
12854
|
}
|
|
12855
|
+
return {
|
|
12856
|
+
globalConnNetId: anchoredPlacement.globalConnNetId,
|
|
12857
|
+
netId: netConnection.netId,
|
|
12858
|
+
pinIds: [pinId],
|
|
12859
|
+
stubTracePath: [start, end],
|
|
12860
|
+
axis,
|
|
12861
|
+
anchorPoint,
|
|
12862
|
+
center,
|
|
12863
|
+
width,
|
|
12864
|
+
height,
|
|
12865
|
+
side
|
|
12866
|
+
};
|
|
12631
12867
|
}
|
|
12632
12868
|
computeInlinePlacement(directConnection) {
|
|
12633
12869
|
const traces = this.tracesByPinPairKey.get(getPinPairKey2(directConnection.pinIds)) ?? [];
|
|
@@ -12657,7 +12893,11 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12657
12893
|
minY: center.y - halfAlong,
|
|
12658
12894
|
maxY: center.y + halfAlong
|
|
12659
12895
|
};
|
|
12660
|
-
if (this.isObstructed(bounds,
|
|
12896
|
+
if (this.isObstructed(bounds, {
|
|
12897
|
+
ownTrace: trace,
|
|
12898
|
+
ownGlobalConnNetId: trace.globalConnNetId
|
|
12899
|
+
}))
|
|
12900
|
+
continue;
|
|
12661
12901
|
return {
|
|
12662
12902
|
globalConnNetId: trace.globalConnNetId,
|
|
12663
12903
|
netId: directConnection.netId,
|
|
@@ -12742,7 +12982,11 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12742
12982
|
minY: center.y - halfAlong,
|
|
12743
12983
|
maxY: center.y + halfAlong
|
|
12744
12984
|
};
|
|
12745
|
-
if (this.isObstructed(bounds,
|
|
12985
|
+
if (this.isObstructed(bounds, {
|
|
12986
|
+
ownTrace: trace,
|
|
12987
|
+
ownGlobalConnNetId: trace.globalConnNetId
|
|
12988
|
+
}))
|
|
12989
|
+
continue;
|
|
12746
12990
|
const anchorPoint = axis === "x" ? { x: along, y: side === "y+" ? maxY : minY } : { x: side === "x-" ? minX : maxX, y: along };
|
|
12747
12991
|
return {
|
|
12748
12992
|
globalConnNetId: trace.globalConnNetId,
|
|
@@ -12764,7 +13008,10 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12764
13008
|
* An inline label may not sit on top of a chip, a component's text, or a
|
|
12765
13009
|
* trace belonging to another net.
|
|
12766
13010
|
*/
|
|
12767
|
-
isObstructed(bounds,
|
|
13011
|
+
isObstructed(bounds, {
|
|
13012
|
+
ownTrace,
|
|
13013
|
+
ownGlobalConnNetId
|
|
13014
|
+
}) {
|
|
12768
13015
|
for (const chip of this.inputProblem.chips) {
|
|
12769
13016
|
const chipBounds = {
|
|
12770
13017
|
minX: chip.center.x - chip.width / 2,
|
|
@@ -12778,9 +13025,9 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12778
13025
|
if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
|
|
12779
13026
|
}
|
|
12780
13027
|
for (const trace of this.traces) {
|
|
12781
|
-
if (trace.mspPairId === ownTrace.mspPairId) continue;
|
|
12782
|
-
if (trace.globalConnNetId ===
|
|
12783
|
-
if (
|
|
13028
|
+
if (ownTrace && trace.mspPairId === ownTrace.mspPairId) continue;
|
|
13029
|
+
if (trace.globalConnNetId === ownGlobalConnNetId) continue;
|
|
13030
|
+
if (doesPathIntersectBounds2(trace.tracePath, bounds)) return true;
|
|
12784
13031
|
}
|
|
12785
13032
|
return false;
|
|
12786
13033
|
}
|
|
@@ -12795,10 +13042,18 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12795
13042
|
}
|
|
12796
13043
|
return keys;
|
|
12797
13044
|
}
|
|
13045
|
+
getOutputTraces(supersededNetLabelKeys) {
|
|
13046
|
+
return this.traces.filter(
|
|
13047
|
+
(trace) => !(supersededNetLabelKeys.has(trace.globalConnNetId) && trace.mspPairId.startsWith("available-net-orientation-"))
|
|
13048
|
+
);
|
|
13049
|
+
}
|
|
12798
13050
|
getOutput() {
|
|
12799
13051
|
const superseded = this.getSupersededNetLabelKeys();
|
|
12800
13052
|
return {
|
|
12801
|
-
|
|
13053
|
+
// AvailableNetOrientationSolver may have routed an elbow from a port to
|
|
13054
|
+
// the anchored label that this inline placement supersedes. Keep the
|
|
13055
|
+
// actual net trace, but discard that now-orphaned label connector.
|
|
13056
|
+
traces: this.getOutputTraces(superseded),
|
|
12802
13057
|
netLabelPlacements: this.inputNetLabelPlacements.filter(
|
|
12803
13058
|
(placement) => !superseded.has(placement.globalConnNetId)
|
|
12804
13059
|
),
|
|
@@ -12811,7 +13066,8 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12811
13066
|
graphics.rects ??= [];
|
|
12812
13067
|
graphics.points ??= [];
|
|
12813
13068
|
graphics.texts ??= [];
|
|
12814
|
-
|
|
13069
|
+
const output = this.getOutput();
|
|
13070
|
+
for (const trace of output.traces) {
|
|
12815
13071
|
graphics.lines.push({
|
|
12816
13072
|
points: trace.tracePath,
|
|
12817
13073
|
strokeColor: "purple"
|
|
@@ -12837,6 +13093,12 @@ orientation: ${label.orientation}`
|
|
|
12837
13093
|
});
|
|
12838
13094
|
}
|
|
12839
13095
|
for (const inlineLabel of this.inlineNetLabelPlacements) {
|
|
13096
|
+
if (inlineLabel.stubTracePath) {
|
|
13097
|
+
graphics.lines.push({
|
|
13098
|
+
points: inlineLabel.stubTracePath,
|
|
13099
|
+
strokeColor: "purple"
|
|
13100
|
+
});
|
|
13101
|
+
}
|
|
12840
13102
|
const isHorizontal4 = inlineLabel.axis === "x";
|
|
12841
13103
|
graphics.rects.push({
|
|
12842
13104
|
center: inlineLabel.center,
|
|
@@ -12851,12 +13113,12 @@ orientation: ${label.orientation}`
|
|
|
12851
13113
|
].join("\n")
|
|
12852
13114
|
});
|
|
12853
13115
|
graphics.texts.push({
|
|
12854
|
-
x: inlineLabel.center.x,
|
|
12855
|
-
y: inlineLabel.center.y,
|
|
13116
|
+
x: inlineLabel.stubTracePath && inlineLabel.axis === "x" ? inlineLabel.center.x + (inlineLabel.stubTracePath[1].x > inlineLabel.stubTracePath[0].x ? -inlineLabel.width / 2 : inlineLabel.width / 2) : inlineLabel.center.x,
|
|
13117
|
+
y: inlineLabel.stubTracePath && inlineLabel.axis === "y" ? inlineLabel.center.y + (inlineLabel.stubTracePath[1].y > inlineLabel.stubTracePath[0].y ? -inlineLabel.width / 2 : inlineLabel.width / 2) : inlineLabel.center.y,
|
|
12856
13118
|
text: inlineLabel.netId ?? "",
|
|
12857
13119
|
color: "green",
|
|
12858
13120
|
fontSize: inlineLabel.height,
|
|
12859
|
-
anchorSide: "center",
|
|
13121
|
+
anchorSide: inlineLabel.stubTracePath ? inlineLabel.stubTracePath[1][inlineLabel.axis] > inlineLabel.stubTracePath[0][inlineLabel.axis] ? "center_left" : "center_right" : "center",
|
|
12860
13122
|
// Vertical labels read bottom-to-top, alongside the wire they name.
|
|
12861
13123
|
rotation: inlineLabel.axis === "y" ? 90 : void 0
|
|
12862
13124
|
});
|
|
@@ -12891,7 +13153,7 @@ var getAnchorCandidates = (segment, labelWidth, step = 0.1) => {
|
|
|
12891
13153
|
offsets.push(slack / 2, -slack / 2);
|
|
12892
13154
|
return offsets.map((offset) => pointAt(mid + offset * direction));
|
|
12893
13155
|
};
|
|
12894
|
-
var
|
|
13156
|
+
var doesPathIntersectBounds2 = (path, bounds) => {
|
|
12895
13157
|
for (let i = 0; i < path.length - 1; i++) {
|
|
12896
13158
|
const a = path[i];
|
|
12897
13159
|
const b = path[i + 1];
|