@tscircuit/schematic-trace-solver 0.0.142 → 0.0.144
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 +21 -16
- package/dist/index.js +111 -62
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +92 -16
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +70 -57
- package/lib/types/InputProblem.ts +7 -4
- package/package.json +1 -1
- package/tests/assets/inline-net-label-two-pin-net.json +67 -0
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +3 -3
- package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +3 -3
- package/tests/repros/repro-tida010076-page02.test.ts +19 -0
- package/tests/repros/repro-usb-power-vbus-label-detour.test.ts +23 -1
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-two-pin-net.snap.svg +57 -0
- package/tests/solvers/InlineNetLabelSolver/inline-net-label-two-pin-net.test.ts +38 -0
- package/tests/solvers/InlineNetLabelSolver/two-pin-terminal-stubs-atomic.test.ts +78 -0
package/dist/index.d.ts
CHANGED
|
@@ -96,7 +96,9 @@ interface InputDirectConnection {
|
|
|
96
96
|
*
|
|
97
97
|
* Only set this for connections whose net name is worth showing on the wire -
|
|
98
98
|
* the solver trusts the caller (e.g. @tscircuit/core) to make that decision.
|
|
99
|
-
*
|
|
99
|
+
* When the connection is routed, the label is placed along the trace. If
|
|
100
|
+
* routing is intentionally skipped, both endpoint labels may instead become
|
|
101
|
+
* outward inline stubs.
|
|
100
102
|
*/
|
|
101
103
|
allowInlineNetLabel?: boolean;
|
|
102
104
|
/**
|
|
@@ -116,9 +118,10 @@ interface InputNetConnection {
|
|
|
116
118
|
netLabelWidth?: number;
|
|
117
119
|
netLabelHeight?: number;
|
|
118
120
|
/**
|
|
119
|
-
* When true, a named
|
|
120
|
-
*
|
|
121
|
-
*
|
|
121
|
+
* When true, a named one- or two-pin net may use inline labels. A single-pin
|
|
122
|
+
* net gets an outward stub. A routed two-pin net gets one label along its
|
|
123
|
+
* trace; when that route is intentionally skipped, both endpoints get
|
|
124
|
+
* outward stubs. Nets with more than two pins retain anchored labels.
|
|
122
125
|
*/
|
|
123
126
|
allowInlineNetLabel?: boolean;
|
|
124
127
|
/** Extent of the inline text along the generated trace stub. */
|
|
@@ -825,6 +828,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
825
828
|
private findValidTraceAnchorCandidate;
|
|
826
829
|
private findValidOutwardTraceAnchorCandidate;
|
|
827
830
|
private getTraceAnchorCandidatePoints;
|
|
831
|
+
private sharesVerticalRailWithAny;
|
|
828
832
|
private getAvailableOrientations;
|
|
829
833
|
private findValidShiftedCandidate;
|
|
830
834
|
/**
|
|
@@ -1199,8 +1203,9 @@ interface InlineNetLabelPlacement {
|
|
|
1199
1203
|
mspPairId?: string;
|
|
1200
1204
|
pinIds: PinId[];
|
|
1201
1205
|
/**
|
|
1202
|
-
* A generated single-ended trace stub. Present
|
|
1203
|
-
*
|
|
1206
|
+
* A generated single-ended trace stub. Present for a single-pin net or for
|
|
1207
|
+
* one endpoint of an unrouted two-pin net; routed point-to-point traces omit
|
|
1208
|
+
* it.
|
|
1204
1209
|
*/
|
|
1205
1210
|
stubTracePath?: [Point, Point];
|
|
1206
1211
|
/** Axis the text runs along: "x" reads left-to-right, "y" reads bottom-to-top */
|
|
@@ -1225,9 +1230,11 @@ interface InlineNetLabelSolverInput {
|
|
|
1225
1230
|
traces: SolvedTracePath[];
|
|
1226
1231
|
netLabelPlacements: NetLabelPlacement[];
|
|
1227
1232
|
}
|
|
1233
|
+
type InlineEligibleConnection = InputDirectConnection | InputNetConnection;
|
|
1228
1234
|
/**
|
|
1229
1235
|
* Places "inline net labels" - net names drawn alongside the trace they belong
|
|
1230
|
-
* to - for
|
|
1236
|
+
* to - for one- or two-pin connections that opted in via
|
|
1237
|
+
* `allowInlineNetLabel`.
|
|
1231
1238
|
*
|
|
1232
1239
|
* Any regular (anchored) net label placement for the same net is dropped, so a
|
|
1233
1240
|
* net is never labeled twice.
|
|
@@ -1237,22 +1244,20 @@ declare class InlineNetLabelSolver extends BaseSolver {
|
|
|
1237
1244
|
traces: SolvedTracePath[];
|
|
1238
1245
|
inputNetLabelPlacements: NetLabelPlacement[];
|
|
1239
1246
|
inlineNetLabelPlacements: InlineNetLabelPlacement[];
|
|
1240
|
-
/**
|
|
1241
|
-
|
|
1242
|
-
/** Single-pin net connections that opted in, still waiting to be processed */
|
|
1243
|
-
queuedPortOnlyNetConnections: InputNetConnection[];
|
|
1247
|
+
/** Eligible one- or two-pin connections still waiting to be processed. */
|
|
1248
|
+
queuedConnections: InlineEligibleConnection[];
|
|
1244
1249
|
private tracesByPinPairKey;
|
|
1245
1250
|
private hasAlignedPortOnlyStubs;
|
|
1246
1251
|
constructor(input: InlineNetLabelSolverInput);
|
|
1247
1252
|
getConstructorParams(): [InlineNetLabelSolverInput];
|
|
1248
1253
|
_step(): void;
|
|
1249
1254
|
/**
|
|
1250
|
-
* Converts
|
|
1251
|
-
*
|
|
1252
|
-
*
|
|
1253
|
-
*
|
|
1255
|
+
* Converts one conventional endpoint placement into an inline label on a
|
|
1256
|
+
* generated outward stub. The stub follows the pin's true facing direction;
|
|
1257
|
+
* an anchored label may finish in another direction after an elbow, which is
|
|
1258
|
+
* not the direction a terminal stub should leave the pin.
|
|
1254
1259
|
*/
|
|
1255
|
-
private
|
|
1260
|
+
private computeTerminalInlinePlacement;
|
|
1256
1261
|
private computeInlinePlacement;
|
|
1257
1262
|
/**
|
|
1258
1263
|
* Places the label over the whole route when the route is straight except
|
package/dist/index.js
CHANGED
|
@@ -8842,13 +8842,14 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8842
8842
|
}
|
|
8843
8843
|
findValidTraceAnchorCandidate(label, orientation, labelIndex) {
|
|
8844
8844
|
const direction = dir(orientation);
|
|
8845
|
-
const candidatePoints = this.getTraceAnchorCandidatePoints(
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
|
|
8845
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(
|
|
8846
|
+
label,
|
|
8847
|
+
orientation
|
|
8848
|
+
).sort((a, b) => {
|
|
8849
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y;
|
|
8850
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y;
|
|
8851
|
+
return bAlongDirection - aAlongDirection;
|
|
8852
|
+
});
|
|
8852
8853
|
for (const anchorPoint of candidatePoints) {
|
|
8853
8854
|
const candidate = this.createCandidate(label, anchorPoint, orientation);
|
|
8854
8855
|
const result = this.evaluateCandidate(
|
|
@@ -8867,13 +8868,14 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8867
8868
|
}
|
|
8868
8869
|
findValidOutwardTraceAnchorCandidate(label, orientation, labelIndex) {
|
|
8869
8870
|
const direction = dir(orientation);
|
|
8870
|
-
const candidatePoints = this.getTraceAnchorCandidatePoints(
|
|
8871
|
-
|
|
8872
|
-
|
|
8873
|
-
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8871
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(
|
|
8872
|
+
label,
|
|
8873
|
+
orientation
|
|
8874
|
+
).sort((a, b) => {
|
|
8875
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y;
|
|
8876
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y;
|
|
8877
|
+
return bAlongDirection - aAlongDirection;
|
|
8878
|
+
});
|
|
8877
8879
|
for (const connectorSource of candidatePoints) {
|
|
8878
8880
|
const preservedColumnAnchor = this.getSearchStartAnchor(
|
|
8879
8881
|
label,
|
|
@@ -8931,10 +8933,40 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8931
8933
|
}
|
|
8932
8934
|
return null;
|
|
8933
8935
|
}
|
|
8934
|
-
getTraceAnchorCandidatePoints(label) {
|
|
8936
|
+
getTraceAnchorCandidatePoints(label, orientation) {
|
|
8935
8937
|
const seen = /* @__PURE__ */ new Set();
|
|
8936
8938
|
const points = [];
|
|
8937
|
-
|
|
8939
|
+
const connectedTraceIds = new Set(label.mspConnectionPairIds ?? []);
|
|
8940
|
+
if (isYOrientation(orientation)) {
|
|
8941
|
+
const connectedPinIds = /* @__PURE__ */ new Set();
|
|
8942
|
+
for (const traceId of connectedTraceIds) {
|
|
8943
|
+
for (const pinId of this.traceMap[traceId]?.pinIds ?? []) {
|
|
8944
|
+
connectedPinIds.add(pinId);
|
|
8945
|
+
}
|
|
8946
|
+
}
|
|
8947
|
+
let foundConnectedTrace = true;
|
|
8948
|
+
while (foundConnectedTrace) {
|
|
8949
|
+
foundConnectedTrace = false;
|
|
8950
|
+
for (const trace of Object.values(this.traceMap)) {
|
|
8951
|
+
if (connectedTraceIds.has(trace.mspPairId)) continue;
|
|
8952
|
+
if (trace.mspPairId.startsWith("available-net-orientation-")) continue;
|
|
8953
|
+
if (trace.globalConnNetId !== label.globalConnNetId) continue;
|
|
8954
|
+
if (!trace.pinIds.some((pinId) => connectedPinIds.has(pinId)))
|
|
8955
|
+
continue;
|
|
8956
|
+
const connectedTraces = [...connectedTraceIds].flatMap((traceId) => {
|
|
8957
|
+
const connectedTrace = this.traceMap[traceId];
|
|
8958
|
+
return connectedTrace ? [connectedTrace] : [];
|
|
8959
|
+
});
|
|
8960
|
+
if (!this.sharesVerticalRailWithAny(trace, connectedTraces)) {
|
|
8961
|
+
continue;
|
|
8962
|
+
}
|
|
8963
|
+
connectedTraceIds.add(trace.mspPairId);
|
|
8964
|
+
for (const pinId of trace.pinIds) connectedPinIds.add(pinId);
|
|
8965
|
+
foundConnectedTrace = true;
|
|
8966
|
+
}
|
|
8967
|
+
}
|
|
8968
|
+
}
|
|
8969
|
+
for (const traceId of connectedTraceIds) {
|
|
8938
8970
|
const trace = this.traceMap[traceId];
|
|
8939
8971
|
if (!trace) continue;
|
|
8940
8972
|
for (const point of trace.tracePath) {
|
|
@@ -8946,6 +8978,26 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
8946
8978
|
}
|
|
8947
8979
|
return points;
|
|
8948
8980
|
}
|
|
8981
|
+
sharesVerticalRailWithAny(trace, otherTraces) {
|
|
8982
|
+
const verticalRailXs = /* @__PURE__ */ new Set();
|
|
8983
|
+
for (const otherTrace of otherTraces) {
|
|
8984
|
+
for (let i = 0; i < otherTrace.tracePath.length - 1; i++) {
|
|
8985
|
+
const start = otherTrace.tracePath[i];
|
|
8986
|
+
const end = otherTrace.tracePath[i + 1];
|
|
8987
|
+
if (Math.abs(start.x - end.x) <= EPS12 && Math.abs(start.y - end.y) > EPS12) {
|
|
8988
|
+
verticalRailXs.add(start.x);
|
|
8989
|
+
}
|
|
8990
|
+
}
|
|
8991
|
+
}
|
|
8992
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
8993
|
+
const start = trace.tracePath[i];
|
|
8994
|
+
const end = trace.tracePath[i + 1];
|
|
8995
|
+
if (Math.abs(start.x - end.x) <= EPS12 && Math.abs(start.y - end.y) > EPS12 && [...verticalRailXs].some((x) => Math.abs(x - start.x) <= EPS12)) {
|
|
8996
|
+
return true;
|
|
8997
|
+
}
|
|
8998
|
+
}
|
|
8999
|
+
return false;
|
|
9000
|
+
}
|
|
8949
9001
|
getAvailableOrientations(label) {
|
|
8950
9002
|
const effectiveNetId = label.netId ?? label.globalConnNetId;
|
|
8951
9003
|
return this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? [];
|
|
@@ -12734,10 +12786,8 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12734
12786
|
traces;
|
|
12735
12787
|
inputNetLabelPlacements;
|
|
12736
12788
|
inlineNetLabelPlacements = [];
|
|
12737
|
-
/**
|
|
12738
|
-
|
|
12739
|
-
/** Single-pin net connections that opted in, still waiting to be processed */
|
|
12740
|
-
queuedPortOnlyNetConnections;
|
|
12789
|
+
/** Eligible one- or two-pin connections still waiting to be processed. */
|
|
12790
|
+
queuedConnections;
|
|
12741
12791
|
tracesByPinPairKey;
|
|
12742
12792
|
hasAlignedPortOnlyStubs = false;
|
|
12743
12793
|
constructor(input) {
|
|
@@ -12745,12 +12795,14 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12745
12795
|
this.inputProblem = input.inputProblem;
|
|
12746
12796
|
this.traces = input.traces;
|
|
12747
12797
|
this.inputNetLabelPlacements = input.netLabelPlacements;
|
|
12748
|
-
this.
|
|
12749
|
-
|
|
12750
|
-
|
|
12751
|
-
|
|
12752
|
-
|
|
12753
|
-
|
|
12798
|
+
this.queuedConnections = [
|
|
12799
|
+
...this.inputProblem.directConnections.filter(
|
|
12800
|
+
(connection) => connection.allowInlineNetLabel && connection.netId
|
|
12801
|
+
),
|
|
12802
|
+
...this.inputProblem.netConnections.filter(
|
|
12803
|
+
(connection) => connection.allowInlineNetLabel && connection.pinIds.length >= 1 && connection.pinIds.length <= 2 && connection.netId
|
|
12804
|
+
)
|
|
12805
|
+
];
|
|
12754
12806
|
this.tracesByPinPairKey = /* @__PURE__ */ new Map();
|
|
12755
12807
|
for (const trace of this.traces) {
|
|
12756
12808
|
const key = getPinPairKey2(trace.pins.map((p) => p.pinId));
|
|
@@ -12772,21 +12824,19 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12772
12824
|
];
|
|
12773
12825
|
}
|
|
12774
12826
|
_step() {
|
|
12775
|
-
const
|
|
12776
|
-
if (
|
|
12777
|
-
const
|
|
12778
|
-
if (
|
|
12779
|
-
this.
|
|
12780
|
-
|
|
12781
|
-
|
|
12782
|
-
|
|
12783
|
-
|
|
12784
|
-
|
|
12785
|
-
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
if (placement) {
|
|
12789
|
-
this.inlineNetLabelPlacements.push(placement);
|
|
12827
|
+
const connection = this.queuedConnections.shift();
|
|
12828
|
+
if (connection) {
|
|
12829
|
+
const routedTraces = connection.pinIds.length === 2 ? this.tracesByPinPairKey.get(getPinPairKey2(connection.pinIds)) ?? [] : [];
|
|
12830
|
+
if (routedTraces.length > 0) {
|
|
12831
|
+
const placement = this.computeInlinePlacement(connection);
|
|
12832
|
+
if (placement) this.inlineNetLabelPlacements.push(placement);
|
|
12833
|
+
} else {
|
|
12834
|
+
const terminalPlacements = connection.pinIds.map(
|
|
12835
|
+
(pinId) => this.computeTerminalInlinePlacement(connection, pinId)
|
|
12836
|
+
);
|
|
12837
|
+
if (terminalPlacements.every((placement) => placement !== null)) {
|
|
12838
|
+
this.inlineNetLabelPlacements.push(...terminalPlacements);
|
|
12839
|
+
}
|
|
12790
12840
|
}
|
|
12791
12841
|
return;
|
|
12792
12842
|
}
|
|
@@ -12804,24 +12854,23 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12804
12854
|
this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length;
|
|
12805
12855
|
}
|
|
12806
12856
|
/**
|
|
12807
|
-
* Converts
|
|
12808
|
-
*
|
|
12809
|
-
*
|
|
12810
|
-
*
|
|
12857
|
+
* Converts one conventional endpoint placement into an inline label on a
|
|
12858
|
+
* generated outward stub. The stub follows the pin's true facing direction;
|
|
12859
|
+
* an anchored label may finish in another direction after an elbow, which is
|
|
12860
|
+
* not the direction a terminal stub should leave the pin.
|
|
12811
12861
|
*/
|
|
12812
|
-
|
|
12813
|
-
|
|
12814
|
-
if (!pinId) return null;
|
|
12862
|
+
computeTerminalInlinePlacement(connection, pinId) {
|
|
12863
|
+
if (!connection.netId) return null;
|
|
12815
12864
|
const anchoredPlacement = this.inputNetLabelPlacements.find(
|
|
12816
|
-
(placement) => placement.netId ===
|
|
12865
|
+
(placement) => placement.netId === connection.netId && placement.pinIds.length === 1 && placement.pinIds[0] === pinId
|
|
12817
12866
|
);
|
|
12818
12867
|
if (!anchoredPlacement) return null;
|
|
12819
12868
|
const inputChip = this.inputProblem.chips.find(
|
|
12820
12869
|
(chip) => chip.pins.some((pin) => pin.pinId === pinId)
|
|
12821
12870
|
);
|
|
12822
12871
|
const inputPin = inputChip?.pins.find((pin) => pin.pinId === pinId);
|
|
12823
|
-
const height =
|
|
12824
|
-
const width =
|
|
12872
|
+
const height = connection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT;
|
|
12873
|
+
const width = connection.inlineNetLabelWidth ?? connection.netLabelWidth ?? estimateInlineNetLabelWidth(connection.netId, height);
|
|
12825
12874
|
const stubLength = Math.max(width + 0.2, 0.6);
|
|
12826
12875
|
const start = inputPin ? { x: inputPin.x, y: inputPin.y } : anchoredPlacement.anchorPoint;
|
|
12827
12876
|
const direction = inputPin && inputChip ? inputPin._facingDirection ?? getPinDirection(inputPin, inputChip) : anchoredPlacement.orientation;
|
|
@@ -12854,7 +12903,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12854
12903
|
}
|
|
12855
12904
|
return {
|
|
12856
12905
|
globalConnNetId: anchoredPlacement.globalConnNetId,
|
|
12857
|
-
netId:
|
|
12906
|
+
netId: connection.netId,
|
|
12858
12907
|
pinIds: [pinId],
|
|
12859
12908
|
stubTracePath: [start, end],
|
|
12860
12909
|
axis,
|
|
@@ -12865,14 +12914,14 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12865
12914
|
side
|
|
12866
12915
|
};
|
|
12867
12916
|
}
|
|
12868
|
-
computeInlinePlacement(
|
|
12869
|
-
const traces = this.tracesByPinPairKey.get(getPinPairKey2(
|
|
12917
|
+
computeInlinePlacement(connection) {
|
|
12918
|
+
const traces = this.tracesByPinPairKey.get(getPinPairKey2(connection.pinIds)) ?? [];
|
|
12870
12919
|
if (traces.length === 0) return null;
|
|
12871
12920
|
const trace = traces[0];
|
|
12872
12921
|
const segments = getAxisAlignedSegments(trace.tracePath);
|
|
12873
12922
|
if (segments.length === 0) return null;
|
|
12874
|
-
const height =
|
|
12875
|
-
const width =
|
|
12923
|
+
const height = connection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT;
|
|
12924
|
+
const width = connection.inlineNetLabelWidth ?? connection.netLabelWidth ?? estimateInlineNetLabelWidth(connection.netId, height);
|
|
12876
12925
|
const offset = height / 2 + INLINE_NET_LABEL_TRACE_MARGIN;
|
|
12877
12926
|
const usableSegments = segments.filter((segment) => segment.length >= width).sort((a, b) => b.length - a.length);
|
|
12878
12927
|
for (const segment of usableSegments) {
|
|
@@ -12900,9 +12949,9 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12900
12949
|
continue;
|
|
12901
12950
|
return {
|
|
12902
12951
|
globalConnNetId: trace.globalConnNetId,
|
|
12903
|
-
netId:
|
|
12952
|
+
netId: connection.netId,
|
|
12904
12953
|
mspPairId: trace.mspPairId,
|
|
12905
|
-
pinIds: [...
|
|
12954
|
+
pinIds: [...connection.pinIds],
|
|
12906
12955
|
axis: segment.axis,
|
|
12907
12956
|
anchorPoint,
|
|
12908
12957
|
center,
|
|
@@ -12915,7 +12964,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12915
12964
|
}
|
|
12916
12965
|
const spanPlacement = this.computeSpanPlacement({
|
|
12917
12966
|
trace,
|
|
12918
|
-
|
|
12967
|
+
connection,
|
|
12919
12968
|
width,
|
|
12920
12969
|
height,
|
|
12921
12970
|
offset
|
|
@@ -12931,7 +12980,7 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12931
12980
|
*/
|
|
12932
12981
|
computeSpanPlacement({
|
|
12933
12982
|
trace,
|
|
12934
|
-
|
|
12983
|
+
connection,
|
|
12935
12984
|
width,
|
|
12936
12985
|
height,
|
|
12937
12986
|
offset
|
|
@@ -12990,9 +13039,9 @@ var InlineNetLabelSolver = class extends BaseSolver {
|
|
|
12990
13039
|
const anchorPoint = axis === "x" ? { x: along, y: side === "y+" ? maxY : minY } : { x: side === "x-" ? minX : maxX, y: along };
|
|
12991
13040
|
return {
|
|
12992
13041
|
globalConnNetId: trace.globalConnNetId,
|
|
12993
|
-
netId:
|
|
13042
|
+
netId: connection.netId,
|
|
12994
13043
|
mspPairId: trace.mspPairId,
|
|
12995
|
-
pinIds: [...
|
|
13044
|
+
pinIds: [...connection.pinIds],
|
|
12996
13045
|
axis,
|
|
12997
13046
|
anchorPoint,
|
|
12998
13047
|
center,
|
|
@@ -635,13 +635,14 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
635
635
|
labelIndex: number,
|
|
636
636
|
) {
|
|
637
637
|
const direction = dir(orientation)
|
|
638
|
-
const candidatePoints = this.getTraceAnchorCandidatePoints(
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
638
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(
|
|
639
|
+
label,
|
|
640
|
+
orientation,
|
|
641
|
+
).sort((a, b) => {
|
|
642
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y
|
|
643
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y
|
|
644
|
+
return bAlongDirection - aAlongDirection
|
|
645
|
+
})
|
|
645
646
|
|
|
646
647
|
for (const anchorPoint of candidatePoints) {
|
|
647
648
|
const candidate = this.createCandidate(label, anchorPoint, orientation)
|
|
@@ -668,13 +669,14 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
668
669
|
labelIndex: number,
|
|
669
670
|
) {
|
|
670
671
|
const direction = dir(orientation)
|
|
671
|
-
const candidatePoints = this.getTraceAnchorCandidatePoints(
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
672
|
+
const candidatePoints = this.getTraceAnchorCandidatePoints(
|
|
673
|
+
label,
|
|
674
|
+
orientation,
|
|
675
|
+
).sort((a, b) => {
|
|
676
|
+
const aAlongDirection = a.x * direction.x + a.y * direction.y
|
|
677
|
+
const bAlongDirection = b.x * direction.x + b.y * direction.y
|
|
678
|
+
return bAlongDirection - aAlongDirection
|
|
679
|
+
})
|
|
678
680
|
|
|
679
681
|
for (const connectorSource of candidatePoints) {
|
|
680
682
|
const preservedColumnAnchor = this.getSearchStartAnchor(
|
|
@@ -742,11 +744,52 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
742
744
|
return null
|
|
743
745
|
}
|
|
744
746
|
|
|
745
|
-
private getTraceAnchorCandidatePoints(
|
|
747
|
+
private getTraceAnchorCandidatePoints(
|
|
748
|
+
label: NetLabelPlacement,
|
|
749
|
+
orientation: FacingDirection,
|
|
750
|
+
) {
|
|
746
751
|
const seen = new Set<string>()
|
|
747
752
|
const points: Point[] = []
|
|
753
|
+
const connectedTraceIds = new Set(label.mspConnectionPairIds ?? [])
|
|
754
|
+
|
|
755
|
+
if (isYOrientation(orientation)) {
|
|
756
|
+
const connectedPinIds = new Set<string>()
|
|
757
|
+
for (const traceId of connectedTraceIds) {
|
|
758
|
+
for (const pinId of this.traceMap[traceId]?.pinIds ?? []) {
|
|
759
|
+
connectedPinIds.add(pinId)
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Multi-pin rails are routed as a chain of pairwise traces. Follow the
|
|
764
|
+
// pairs only when they share both a pin and an aligned vertical rail,
|
|
765
|
+
// which identifies one continuous rail. A shared pin alone can join
|
|
766
|
+
// unrelated branches whose rail offsets are far apart; walking those
|
|
767
|
+
// branches would extend the label connector across the schematic.
|
|
768
|
+
let foundConnectedTrace = true
|
|
769
|
+
while (foundConnectedTrace) {
|
|
770
|
+
foundConnectedTrace = false
|
|
771
|
+
for (const trace of Object.values(this.traceMap)) {
|
|
772
|
+
if (connectedTraceIds.has(trace.mspPairId)) continue
|
|
773
|
+
if (trace.mspPairId.startsWith("available-net-orientation-")) continue
|
|
774
|
+
if (trace.globalConnNetId !== label.globalConnNetId) continue
|
|
775
|
+
if (!trace.pinIds.some((pinId) => connectedPinIds.has(pinId)))
|
|
776
|
+
continue
|
|
777
|
+
const connectedTraces = [...connectedTraceIds].flatMap((traceId) => {
|
|
778
|
+
const connectedTrace = this.traceMap[traceId]
|
|
779
|
+
return connectedTrace ? [connectedTrace] : []
|
|
780
|
+
})
|
|
781
|
+
if (!this.sharesVerticalRailWithAny(trace, connectedTraces)) {
|
|
782
|
+
continue
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
connectedTraceIds.add(trace.mspPairId)
|
|
786
|
+
for (const pinId of trace.pinIds) connectedPinIds.add(pinId)
|
|
787
|
+
foundConnectedTrace = true
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
748
791
|
|
|
749
|
-
for (const traceId of
|
|
792
|
+
for (const traceId of connectedTraceIds) {
|
|
750
793
|
const trace = this.traceMap[traceId]
|
|
751
794
|
if (!trace) continue
|
|
752
795
|
for (const point of trace.tracePath) {
|
|
@@ -760,6 +803,39 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
760
803
|
return points
|
|
761
804
|
}
|
|
762
805
|
|
|
806
|
+
private sharesVerticalRailWithAny(
|
|
807
|
+
trace: SolvedTracePath,
|
|
808
|
+
otherTraces: SolvedTracePath[],
|
|
809
|
+
) {
|
|
810
|
+
const verticalRailXs = new Set<number>()
|
|
811
|
+
for (const otherTrace of otherTraces) {
|
|
812
|
+
for (let i = 0; i < otherTrace.tracePath.length - 1; i++) {
|
|
813
|
+
const start = otherTrace.tracePath[i]!
|
|
814
|
+
const end = otherTrace.tracePath[i + 1]!
|
|
815
|
+
if (
|
|
816
|
+
Math.abs(start.x - end.x) <= EPS &&
|
|
817
|
+
Math.abs(start.y - end.y) > EPS
|
|
818
|
+
) {
|
|
819
|
+
verticalRailXs.add(start.x)
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
825
|
+
const start = trace.tracePath[i]!
|
|
826
|
+
const end = trace.tracePath[i + 1]!
|
|
827
|
+
if (
|
|
828
|
+
Math.abs(start.x - end.x) <= EPS &&
|
|
829
|
+
Math.abs(start.y - end.y) > EPS &&
|
|
830
|
+
[...verticalRailXs].some((x) => Math.abs(x - start.x) <= EPS)
|
|
831
|
+
) {
|
|
832
|
+
return true
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
return false
|
|
837
|
+
}
|
|
838
|
+
|
|
763
839
|
private getAvailableOrientations(label: NetLabelPlacement) {
|
|
764
840
|
const effectiveNetId = label.netId ?? label.globalConnNetId
|
|
765
841
|
return this.inputProblem.availableNetLabelOrientations[effectiveNetId] ?? []
|