@tscircuit/schematic-trace-solver 0.0.164 → 0.0.165
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 +5 -1
- package/dist/index.js +152 -19
- package/lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts +225 -24
- package/package.json +1 -1
- package/tests/assets/example51.json +6 -0
- package/tests/examples/__snapshots__/example51.snap.svg +42 -78
- package/tests/examples/example51.test.ts +24 -0
- package/tests/fixtures/convertSolverOutputToCircuitJson.ts +22 -3
- package/tests/repros/__snapshots__/repro-bluetooth-controller-ground-decoupling-groups.snap.svg +1 -15
- package/tests/repros/__snapshots__/repro-core-ground-inline-label-fallback.snap.svg +2 -16
- package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +1 -15
- package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +16 -30
package/dist/index.d.ts
CHANGED
|
@@ -1394,11 +1394,15 @@ declare class NetLabelToTraceSolver extends BaseSolver {
|
|
|
1394
1394
|
activeSubSolver: SchematicTraceSingleLineSolver2 | null;
|
|
1395
1395
|
constructor(input: InlineNetLabelOutput);
|
|
1396
1396
|
getConstructorParams(): [InlineNetLabelOutput];
|
|
1397
|
-
private
|
|
1397
|
+
private isPortOnlyFallbackLabel;
|
|
1398
|
+
private isDirectConnectionLabel;
|
|
1399
|
+
private getMultiPinNetConnection;
|
|
1398
1400
|
private buildCandidatePairs;
|
|
1401
|
+
private buildRoutedComponentCandidates;
|
|
1399
1402
|
private isSupersededConnectorTrace;
|
|
1400
1403
|
private routeIntersectsRemainingLabels;
|
|
1401
1404
|
private tryAcceptCurrentRoute;
|
|
1405
|
+
private areCandidatePinsAlreadyConnected;
|
|
1402
1406
|
_step(): void;
|
|
1403
1407
|
getOutput(): {
|
|
1404
1408
|
traces: SolvedTracePath[];
|
package/dist/index.js
CHANGED
|
@@ -15051,6 +15051,8 @@ var getTraceRecoveryConnectivityMaps = (inputProblem) => {
|
|
|
15051
15051
|
// lib/solvers/NetLabelToTraceSolver/NetLabelToTraceSolver.ts
|
|
15052
15052
|
var AVAILABLE_NET_ORIENTATION_PREFIX = "available-net-orientation-";
|
|
15053
15053
|
var RECOVERED_TRACE_PREFIX = "net-label-to-trace-";
|
|
15054
|
+
var MAX_NAMED_NET_RECOVERY_PERPENDICULAR_OFFSET = 0.05;
|
|
15055
|
+
var MAX_ROUTED_COMPONENT_RECOVERY_PERPENDICULAR_OFFSET = 0.25;
|
|
15054
15056
|
var getCanonicalPairKey = (firstPinId, secondPinId) => [firstPinId, secondPinId].sort().join("--");
|
|
15055
15057
|
var pathIntersectsRenderedLabel = (path, label) => {
|
|
15056
15058
|
let width = label.width;
|
|
@@ -15071,12 +15073,21 @@ var pathIntersectsRenderedLabel = (path, label) => {
|
|
|
15071
15073
|
}
|
|
15072
15074
|
return false;
|
|
15073
15075
|
};
|
|
15074
|
-
var getPerpendicularOffset = (
|
|
15075
|
-
const xDistance = Math.abs(
|
|
15076
|
-
const yDistance = Math.abs(
|
|
15076
|
+
var getPerpendicularOffset = (firstPoint, secondPoint) => {
|
|
15077
|
+
const xDistance = Math.abs(firstPoint.x - secondPoint.x);
|
|
15078
|
+
const yDistance = Math.abs(firstPoint.y - secondPoint.y);
|
|
15077
15079
|
if (xDistance >= yDistance) return yDistance;
|
|
15078
15080
|
return xDistance;
|
|
15079
15081
|
};
|
|
15082
|
+
var arePinsCoFacingAlongSeparationAxis = (firstPin, secondPin) => {
|
|
15083
|
+
if (firstPin._facingDirection !== secondPin._facingDirection) return false;
|
|
15084
|
+
const xDistance = Math.abs(firstPin.x - secondPin.x);
|
|
15085
|
+
const yDistance = Math.abs(firstPin.y - secondPin.y);
|
|
15086
|
+
if (xDistance >= yDistance) {
|
|
15087
|
+
return firstPin._facingDirection === "x+" || firstPin._facingDirection === "x-";
|
|
15088
|
+
}
|
|
15089
|
+
return firstPin._facingDirection === "y+" || firstPin._facingDirection === "y-";
|
|
15090
|
+
};
|
|
15080
15091
|
var NetLabelToTraceSolver = class extends BaseSolver {
|
|
15081
15092
|
constructor(input) {
|
|
15082
15093
|
super();
|
|
@@ -15104,26 +15115,36 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15104
15115
|
getConstructorParams() {
|
|
15105
15116
|
return [this.input];
|
|
15106
15117
|
}
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15118
|
+
isPortOnlyFallbackLabel(label, groundGlobalConnNetIds) {
|
|
15119
|
+
return !(label.pinIds.length !== 1 || label.mspConnectionPairIds.length !== 0 || !label.netId || groundGlobalConnNetIds.has(label.globalConnNetId));
|
|
15120
|
+
}
|
|
15121
|
+
isDirectConnectionLabel(label) {
|
|
15122
|
+
if (!label.netId || label.pinIds.length !== 1) return false;
|
|
15111
15123
|
const pinId = label.pinIds[0];
|
|
15112
15124
|
return this.inputProblem.directConnections.some(
|
|
15113
15125
|
(connection) => connection.netId === label.netId && connection.pinIds.includes(pinId)
|
|
15114
15126
|
);
|
|
15115
15127
|
}
|
|
15128
|
+
getMultiPinNetConnection(label) {
|
|
15129
|
+
if (!label.netId || label.pinIds.length !== 1) return void 0;
|
|
15130
|
+
const pinId = label.pinIds[0];
|
|
15131
|
+
return this.inputProblem.netConnections.find(
|
|
15132
|
+
(connection) => connection.isGround === false && connection.pinIds.length > 2 && connection.netId === label.netId && connection.pinIds.includes(pinId)
|
|
15133
|
+
);
|
|
15134
|
+
}
|
|
15116
15135
|
buildCandidatePairs() {
|
|
15117
15136
|
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
15118
15137
|
this.inputProblem
|
|
15119
15138
|
);
|
|
15120
|
-
const
|
|
15139
|
+
const groundGlobalConnNetIds = /* @__PURE__ */ new Set();
|
|
15140
|
+
for (const connection of this.inputProblem.netConnections) {
|
|
15141
|
+
if (!connection.isGround) continue;
|
|
15142
|
+
const globalConnNetId = netConnMap.getNetConnectedToId(connection.netId);
|
|
15143
|
+
if (globalConnNetId) groundGlobalConnNetIds.add(globalConnNetId);
|
|
15144
|
+
}
|
|
15121
15145
|
const labelsByGlobalNet = /* @__PURE__ */ new Map();
|
|
15122
15146
|
for (const label of this.input.netLabelPlacements) {
|
|
15123
|
-
if (!this.
|
|
15124
|
-
label,
|
|
15125
|
-
groundGlobalConnNetId
|
|
15126
|
-
)) {
|
|
15147
|
+
if (!this.isPortOnlyFallbackLabel(label, groundGlobalConnNetIds) || !this.isDirectConnectionLabel(label) && !this.getMultiPinNetConnection(label)) {
|
|
15127
15148
|
continue;
|
|
15128
15149
|
}
|
|
15129
15150
|
const labels = labelsByGlobalNet.get(label.globalConnNetId) ?? [];
|
|
@@ -15139,6 +15160,16 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15139
15160
|
const firstPin = this.pinMap.get(firstLabel.pinIds[0]);
|
|
15140
15161
|
const secondPin = this.pinMap.get(secondLabel.pinIds[0]);
|
|
15141
15162
|
if (!firstPin || !secondPin) continue;
|
|
15163
|
+
const perpendicularOffset = getPerpendicularOffset(
|
|
15164
|
+
firstPin,
|
|
15165
|
+
secondPin
|
|
15166
|
+
);
|
|
15167
|
+
const bothLabelsBelongToDirectConnections = this.isDirectConnectionLabel(firstLabel) && this.isDirectConnectionLabel(secondLabel);
|
|
15168
|
+
const firstMultiPinNetConnection = this.getMultiPinNetConnection(firstLabel);
|
|
15169
|
+
const secondMultiPinNetConnection = this.getMultiPinNetConnection(secondLabel);
|
|
15170
|
+
if (!bothLabelsBelongToDirectConnections && (!firstMultiPinNetConnection || firstMultiPinNetConnection !== secondMultiPinNetConnection || perpendicularOffset > MAX_NAMED_NET_RECOVERY_PERPENDICULAR_OFFSET)) {
|
|
15171
|
+
continue;
|
|
15172
|
+
}
|
|
15142
15173
|
if (arePinsInDifferentSchematicSections(
|
|
15143
15174
|
this.inputProblem,
|
|
15144
15175
|
firstPin,
|
|
@@ -15159,18 +15190,103 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15159
15190
|
firstLabel,
|
|
15160
15191
|
secondLabel,
|
|
15161
15192
|
pins: [firstPin, secondPin],
|
|
15162
|
-
perpendicularOffset
|
|
15193
|
+
perpendicularOffset,
|
|
15163
15194
|
routeDistance: Math.abs(firstPin.x - secondPin.x) + Math.abs(firstPin.y - secondPin.y),
|
|
15164
|
-
key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId)
|
|
15195
|
+
key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
|
|
15196
|
+
recoveryMode: "fallback_labels"
|
|
15165
15197
|
});
|
|
15166
15198
|
}
|
|
15167
15199
|
}
|
|
15168
15200
|
}
|
|
15201
|
+
candidates.push(...this.buildRoutedComponentCandidates());
|
|
15169
15202
|
candidates.sort(
|
|
15170
15203
|
(first, second) => first.perpendicularOffset - second.perpendicularOffset || first.routeDistance - second.routeDistance || first.key.localeCompare(second.key)
|
|
15171
15204
|
);
|
|
15172
15205
|
return candidates;
|
|
15173
15206
|
}
|
|
15207
|
+
buildRoutedComponentCandidates() {
|
|
15208
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
15209
|
+
this.inputProblem
|
|
15210
|
+
);
|
|
15211
|
+
const candidates = [];
|
|
15212
|
+
for (const connection of this.inputProblem.netConnections) {
|
|
15213
|
+
if (connection.pinIds.length <= 2 || connection.isGround !== false)
|
|
15214
|
+
continue;
|
|
15215
|
+
const globalConnNetId = netConnMap.getNetConnectedToId(connection.netId);
|
|
15216
|
+
if (!globalConnNetId) continue;
|
|
15217
|
+
const traceConnectedPinComponents = getTraceConnectedPinComponents({
|
|
15218
|
+
pinIds: connection.pinIds,
|
|
15219
|
+
traces: this.outputTraces.filter(
|
|
15220
|
+
(trace) => trace.globalConnNetId === globalConnNetId
|
|
15221
|
+
)
|
|
15222
|
+
}).filter((component) => component.traces.length > 0);
|
|
15223
|
+
for (let firstIndex = 0; firstIndex < traceConnectedPinComponents.length; firstIndex++) {
|
|
15224
|
+
for (let secondIndex = firstIndex + 1; secondIndex < traceConnectedPinComponents.length; secondIndex++) {
|
|
15225
|
+
const firstComponent = traceConnectedPinComponents[firstIndex];
|
|
15226
|
+
const secondComponent = traceConnectedPinComponents[secondIndex];
|
|
15227
|
+
const firstLabel = this.input.netLabelPlacements.find(
|
|
15228
|
+
(label) => label.globalConnNetId === globalConnNetId && label.mspConnectionPairIds.length > 0 && label.pinIds.some(
|
|
15229
|
+
(pinId) => firstComponent.pinIds.includes(pinId)
|
|
15230
|
+
)
|
|
15231
|
+
);
|
|
15232
|
+
const secondLabel = this.input.netLabelPlacements.find(
|
|
15233
|
+
(label) => label.globalConnNetId === globalConnNetId && label.mspConnectionPairIds.length > 0 && label.pinIds.some(
|
|
15234
|
+
(pinId) => secondComponent.pinIds.includes(pinId)
|
|
15235
|
+
)
|
|
15236
|
+
);
|
|
15237
|
+
if (!firstLabel || !secondLabel || firstLabel === secondLabel)
|
|
15238
|
+
continue;
|
|
15239
|
+
if (getPerpendicularOffset(
|
|
15240
|
+
firstLabel.anchorPoint,
|
|
15241
|
+
secondLabel.anchorPoint
|
|
15242
|
+
) > MAX_ROUTED_COMPONENT_RECOVERY_PERPENDICULAR_OFFSET) {
|
|
15243
|
+
continue;
|
|
15244
|
+
}
|
|
15245
|
+
let bestCandidate;
|
|
15246
|
+
for (const firstPinId of firstComponent.pinIds) {
|
|
15247
|
+
for (const secondPinId of secondComponent.pinIds) {
|
|
15248
|
+
const firstPin = this.pinMap.get(firstPinId);
|
|
15249
|
+
const secondPin = this.pinMap.get(secondPinId);
|
|
15250
|
+
if (!firstPin || !secondPin) continue;
|
|
15251
|
+
const perpendicularOffset = getPerpendicularOffset(
|
|
15252
|
+
firstPin,
|
|
15253
|
+
secondPin
|
|
15254
|
+
);
|
|
15255
|
+
if (perpendicularOffset > MAX_ROUTED_COMPONENT_RECOVERY_PERPENDICULAR_OFFSET || arePinsCoFacingAlongSeparationAxis(firstPin, secondPin) || arePinsInDifferentSchematicSections(
|
|
15256
|
+
this.inputProblem,
|
|
15257
|
+
firstPin,
|
|
15258
|
+
secondPin
|
|
15259
|
+
) || doesPairCrossRestrictedCenterLines({
|
|
15260
|
+
inputProblem: this.inputProblem,
|
|
15261
|
+
chipMap: this.chipMap,
|
|
15262
|
+
pinIdMap: this.pinMap,
|
|
15263
|
+
p1: firstPin,
|
|
15264
|
+
p2: secondPin
|
|
15265
|
+
})) {
|
|
15266
|
+
continue;
|
|
15267
|
+
}
|
|
15268
|
+
const routeDistance = Math.abs(firstPin.x - secondPin.x) + Math.abs(firstPin.y - secondPin.y);
|
|
15269
|
+
const candidate = {
|
|
15270
|
+
firstLabel,
|
|
15271
|
+
secondLabel,
|
|
15272
|
+
pins: [firstPin, secondPin],
|
|
15273
|
+
perpendicularOffset,
|
|
15274
|
+
routeDistance,
|
|
15275
|
+
key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
|
|
15276
|
+
recoveryMode: "routed_components",
|
|
15277
|
+
netConnectionPinIds: connection.pinIds
|
|
15278
|
+
};
|
|
15279
|
+
if (!bestCandidate || candidate.routeDistance < bestCandidate.routeDistance || candidate.routeDistance === bestCandidate.routeDistance && (candidate.perpendicularOffset < bestCandidate.perpendicularOffset || candidate.perpendicularOffset === bestCandidate.perpendicularOffset && candidate.key.localeCompare(bestCandidate.key) < 0)) {
|
|
15280
|
+
bestCandidate = candidate;
|
|
15281
|
+
}
|
|
15282
|
+
}
|
|
15283
|
+
}
|
|
15284
|
+
if (bestCandidate) candidates.push(bestCandidate);
|
|
15285
|
+
}
|
|
15286
|
+
}
|
|
15287
|
+
}
|
|
15288
|
+
return candidates;
|
|
15289
|
+
}
|
|
15174
15290
|
isSupersededConnectorTrace(trace, candidate) {
|
|
15175
15291
|
if (!trace.mspPairId.startsWith(AVAILABLE_NET_ORIENTATION_PREFIX)) {
|
|
15176
15292
|
return false;
|
|
@@ -15196,7 +15312,13 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15196
15312
|
const retainedTraces = this.outputTraces.filter(
|
|
15197
15313
|
(trace) => !this.isSupersededConnectorTrace(trace, candidate)
|
|
15198
15314
|
);
|
|
15199
|
-
|
|
15315
|
+
let collisionTraces = retainedTraces;
|
|
15316
|
+
if (candidate.recoveryMode === "routed_components") {
|
|
15317
|
+
collisionTraces = retainedTraces.filter(
|
|
15318
|
+
(trace) => trace.globalConnNetId !== candidate.firstLabel.globalConnNetId
|
|
15319
|
+
);
|
|
15320
|
+
}
|
|
15321
|
+
if (doesTraceOverlapWithExistingTraces(tracePath, collisionTraces) || this.routeIntersectsRemainingLabels(tracePath, candidate)) {
|
|
15200
15322
|
return;
|
|
15201
15323
|
}
|
|
15202
15324
|
const [firstPin, secondPin] = candidate.pins;
|
|
@@ -15211,11 +15333,22 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15211
15333
|
pinIds: [firstPin.pinId, secondPin.pinId]
|
|
15212
15334
|
};
|
|
15213
15335
|
this.outputTraces = [...retainedTraces, recoveredTrace];
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
15336
|
+
if (candidate.recoveryMode === "fallback_labels") {
|
|
15337
|
+
this.outputNetLabelPlacements = this.outputNetLabelPlacements.filter(
|
|
15338
|
+
(label) => label !== candidate.firstLabel && label !== candidate.secondLabel
|
|
15339
|
+
);
|
|
15340
|
+
}
|
|
15217
15341
|
this.stats.recoveredTraceCount++;
|
|
15218
15342
|
}
|
|
15343
|
+
areCandidatePinsAlreadyConnected(candidate) {
|
|
15344
|
+
if (!candidate.netConnectionPinIds) return false;
|
|
15345
|
+
return getTraceConnectedPinComponents({
|
|
15346
|
+
pinIds: candidate.netConnectionPinIds,
|
|
15347
|
+
traces: this.outputTraces
|
|
15348
|
+
}).some(
|
|
15349
|
+
(component) => component.pinIds.includes(candidate.pins[0].pinId) && component.pinIds.includes(candidate.pins[1].pinId)
|
|
15350
|
+
);
|
|
15351
|
+
}
|
|
15219
15352
|
_step() {
|
|
15220
15353
|
if (this.activeSubSolver) {
|
|
15221
15354
|
this.activeSubSolver.step();
|
|
@@ -15230,7 +15363,7 @@ var NetLabelToTraceSolver = class extends BaseSolver {
|
|
|
15230
15363
|
return;
|
|
15231
15364
|
}
|
|
15232
15365
|
let candidate = this.queuedCandidates.shift();
|
|
15233
|
-
while (candidate && (!this.outputNetLabelPlacements.includes(candidate.firstLabel) || !this.outputNetLabelPlacements.includes(candidate.secondLabel))) {
|
|
15366
|
+
while (candidate && (!this.outputNetLabelPlacements.includes(candidate.firstLabel) || !this.outputNetLabelPlacements.includes(candidate.secondLabel) || this.areCandidatePinsAlreadyConnected(candidate))) {
|
|
15234
15367
|
candidate = this.queuedCandidates.shift();
|
|
15235
15368
|
}
|
|
15236
15369
|
if (!candidate) {
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
getTraceRecoveryConnectivityMaps,
|
|
13
13
|
type TraceRecoveryPin,
|
|
14
14
|
} from "lib/solvers/NetLabelTraceRecovery/getTraceRecoveryConnectivityMaps"
|
|
15
|
+
import { getTraceConnectedPinComponents } from "lib/solvers/SchematicTraceLinesSolver/getTraceConnectedPinComponents"
|
|
15
16
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
16
17
|
import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
|
|
17
18
|
import type {
|
|
@@ -37,10 +38,14 @@ interface CandidatePair {
|
|
|
37
38
|
perpendicularOffset: number
|
|
38
39
|
routeDistance: number
|
|
39
40
|
key: string
|
|
41
|
+
recoveryMode: "fallback_labels" | "routed_components"
|
|
42
|
+
netConnectionPinIds?: PinId[]
|
|
40
43
|
}
|
|
41
44
|
|
|
42
45
|
const AVAILABLE_NET_ORIENTATION_PREFIX = "available-net-orientation-"
|
|
43
46
|
const RECOVERED_TRACE_PREFIX = "net-label-to-trace-"
|
|
47
|
+
const MAX_NAMED_NET_RECOVERY_PERPENDICULAR_OFFSET = 0.05
|
|
48
|
+
const MAX_ROUTED_COMPONENT_RECOVERY_PERPENDICULAR_OFFSET = 0.25
|
|
44
49
|
|
|
45
50
|
const getCanonicalPairKey = (firstPinId: PinId, secondPinId: PinId) =>
|
|
46
51
|
[firstPinId, secondPinId].sort().join("--")
|
|
@@ -70,14 +75,28 @@ export const pathIntersectsRenderedLabel = (
|
|
|
70
75
|
return false
|
|
71
76
|
}
|
|
72
77
|
|
|
73
|
-
const getPerpendicularOffset = (
|
|
78
|
+
const getPerpendicularOffset = (firstPoint: Point, secondPoint: Point) => {
|
|
79
|
+
const xDistance = Math.abs(firstPoint.x - secondPoint.x)
|
|
80
|
+
const yDistance = Math.abs(firstPoint.y - secondPoint.y)
|
|
81
|
+
if (xDistance >= yDistance) return yDistance
|
|
82
|
+
return xDistance
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const arePinsCoFacingAlongSeparationAxis = (
|
|
74
86
|
firstPin: TraceRecoveryPin,
|
|
75
87
|
secondPin: TraceRecoveryPin,
|
|
76
88
|
) => {
|
|
89
|
+
if (firstPin._facingDirection !== secondPin._facingDirection) return false
|
|
77
90
|
const xDistance = Math.abs(firstPin.x - secondPin.x)
|
|
78
91
|
const yDistance = Math.abs(firstPin.y - secondPin.y)
|
|
79
|
-
if (xDistance >= yDistance)
|
|
80
|
-
|
|
92
|
+
if (xDistance >= yDistance) {
|
|
93
|
+
return (
|
|
94
|
+
firstPin._facingDirection === "x+" || firstPin._facingDirection === "x-"
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
return (
|
|
98
|
+
firstPin._facingDirection === "y+" || firstPin._facingDirection === "y-"
|
|
99
|
+
)
|
|
81
100
|
}
|
|
82
101
|
|
|
83
102
|
export class NetLabelToTraceSolver extends BaseSolver {
|
|
@@ -113,20 +132,20 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
113
132
|
return [this.input]
|
|
114
133
|
}
|
|
115
134
|
|
|
116
|
-
private
|
|
135
|
+
private isPortOnlyFallbackLabel(
|
|
117
136
|
label: NetLabelPlacement,
|
|
118
|
-
|
|
137
|
+
groundGlobalConnNetIds: Set<GlobalConnNetId>,
|
|
119
138
|
) {
|
|
120
|
-
|
|
139
|
+
return !(
|
|
121
140
|
label.pinIds.length !== 1 ||
|
|
122
141
|
label.mspConnectionPairIds.length !== 0 ||
|
|
123
142
|
!label.netId ||
|
|
124
|
-
label.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return false
|
|
128
|
-
}
|
|
143
|
+
groundGlobalConnNetIds.has(label.globalConnNetId)
|
|
144
|
+
)
|
|
145
|
+
}
|
|
129
146
|
|
|
147
|
+
private isDirectConnectionLabel(label: NetLabelPlacement) {
|
|
148
|
+
if (!label.netId || label.pinIds.length !== 1) return false
|
|
130
149
|
const pinId = label.pinIds[0]!
|
|
131
150
|
return this.inputProblem.directConnections.some(
|
|
132
151
|
(connection) =>
|
|
@@ -134,20 +153,35 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
134
153
|
)
|
|
135
154
|
}
|
|
136
155
|
|
|
156
|
+
private getMultiPinNetConnection(label: NetLabelPlacement) {
|
|
157
|
+
if (!label.netId || label.pinIds.length !== 1) return undefined
|
|
158
|
+
const pinId = label.pinIds[0]!
|
|
159
|
+
return this.inputProblem.netConnections.find(
|
|
160
|
+
(connection) =>
|
|
161
|
+
connection.isGround === false &&
|
|
162
|
+
connection.pinIds.length > 2 &&
|
|
163
|
+
connection.netId === label.netId &&
|
|
164
|
+
connection.pinIds.includes(pinId),
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
|
|
137
168
|
private buildCandidatePairs() {
|
|
138
169
|
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
139
170
|
this.inputProblem,
|
|
140
171
|
)
|
|
141
|
-
const
|
|
142
|
-
|
|
172
|
+
const groundGlobalConnNetIds = new Set<GlobalConnNetId>()
|
|
173
|
+
for (const connection of this.inputProblem.netConnections) {
|
|
174
|
+
if (!connection.isGround) continue
|
|
175
|
+
const globalConnNetId = netConnMap.getNetConnectedToId(connection.netId)
|
|
176
|
+
if (globalConnNetId) groundGlobalConnNetIds.add(globalConnNetId)
|
|
177
|
+
}
|
|
143
178
|
const labelsByGlobalNet = new Map<GlobalConnNetId, NetLabelPlacement[]>()
|
|
144
179
|
|
|
145
180
|
for (const label of this.input.netLabelPlacements) {
|
|
146
181
|
if (
|
|
147
|
-
!this.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
)
|
|
182
|
+
!this.isPortOnlyFallbackLabel(label, groundGlobalConnNetIds) ||
|
|
183
|
+
(!this.isDirectConnectionLabel(label) &&
|
|
184
|
+
!this.getMultiPinNetConnection(label))
|
|
151
185
|
) {
|
|
152
186
|
continue
|
|
153
187
|
}
|
|
@@ -169,6 +203,25 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
169
203
|
const firstPin = this.pinMap.get(firstLabel.pinIds[0]!)
|
|
170
204
|
const secondPin = this.pinMap.get(secondLabel.pinIds[0]!)
|
|
171
205
|
if (!firstPin || !secondPin) continue
|
|
206
|
+
const perpendicularOffset = getPerpendicularOffset(
|
|
207
|
+
firstPin,
|
|
208
|
+
secondPin,
|
|
209
|
+
)
|
|
210
|
+
const bothLabelsBelongToDirectConnections =
|
|
211
|
+
this.isDirectConnectionLabel(firstLabel) &&
|
|
212
|
+
this.isDirectConnectionLabel(secondLabel)
|
|
213
|
+
const firstMultiPinNetConnection =
|
|
214
|
+
this.getMultiPinNetConnection(firstLabel)
|
|
215
|
+
const secondMultiPinNetConnection =
|
|
216
|
+
this.getMultiPinNetConnection(secondLabel)
|
|
217
|
+
if (
|
|
218
|
+
!bothLabelsBelongToDirectConnections &&
|
|
219
|
+
(!firstMultiPinNetConnection ||
|
|
220
|
+
firstMultiPinNetConnection !== secondMultiPinNetConnection ||
|
|
221
|
+
perpendicularOffset > MAX_NAMED_NET_RECOVERY_PERPENDICULAR_OFFSET)
|
|
222
|
+
) {
|
|
223
|
+
continue
|
|
224
|
+
}
|
|
172
225
|
if (
|
|
173
226
|
arePinsInDifferentSchematicSections(
|
|
174
227
|
this.inputProblem,
|
|
@@ -194,16 +247,19 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
194
247
|
firstLabel,
|
|
195
248
|
secondLabel,
|
|
196
249
|
pins: [firstPin, secondPin],
|
|
197
|
-
perpendicularOffset
|
|
250
|
+
perpendicularOffset,
|
|
198
251
|
routeDistance:
|
|
199
252
|
Math.abs(firstPin.x - secondPin.x) +
|
|
200
253
|
Math.abs(firstPin.y - secondPin.y),
|
|
201
254
|
key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
|
|
255
|
+
recoveryMode: "fallback_labels",
|
|
202
256
|
})
|
|
203
257
|
}
|
|
204
258
|
}
|
|
205
259
|
}
|
|
206
260
|
|
|
261
|
+
candidates.push(...this.buildRoutedComponentCandidates())
|
|
262
|
+
|
|
207
263
|
candidates.sort(
|
|
208
264
|
(first, second) =>
|
|
209
265
|
first.perpendicularOffset - second.perpendicularOffset ||
|
|
@@ -213,6 +269,129 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
213
269
|
return candidates
|
|
214
270
|
}
|
|
215
271
|
|
|
272
|
+
private buildRoutedComponentCandidates() {
|
|
273
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(
|
|
274
|
+
this.inputProblem,
|
|
275
|
+
)
|
|
276
|
+
const candidates: CandidatePair[] = []
|
|
277
|
+
|
|
278
|
+
for (const connection of this.inputProblem.netConnections) {
|
|
279
|
+
if (connection.pinIds.length <= 2 || connection.isGround !== false)
|
|
280
|
+
continue
|
|
281
|
+
const globalConnNetId = netConnMap.getNetConnectedToId(connection.netId)
|
|
282
|
+
if (!globalConnNetId) continue
|
|
283
|
+
|
|
284
|
+
const traceConnectedPinComponents = getTraceConnectedPinComponents({
|
|
285
|
+
pinIds: connection.pinIds,
|
|
286
|
+
traces: this.outputTraces.filter(
|
|
287
|
+
(trace) => trace.globalConnNetId === globalConnNetId,
|
|
288
|
+
),
|
|
289
|
+
}).filter((component) => component.traces.length > 0)
|
|
290
|
+
|
|
291
|
+
for (
|
|
292
|
+
let firstIndex = 0;
|
|
293
|
+
firstIndex < traceConnectedPinComponents.length;
|
|
294
|
+
firstIndex++
|
|
295
|
+
) {
|
|
296
|
+
for (
|
|
297
|
+
let secondIndex = firstIndex + 1;
|
|
298
|
+
secondIndex < traceConnectedPinComponents.length;
|
|
299
|
+
secondIndex++
|
|
300
|
+
) {
|
|
301
|
+
const firstComponent = traceConnectedPinComponents[firstIndex]!
|
|
302
|
+
const secondComponent = traceConnectedPinComponents[secondIndex]!
|
|
303
|
+
const firstLabel = this.input.netLabelPlacements.find(
|
|
304
|
+
(label) =>
|
|
305
|
+
label.globalConnNetId === globalConnNetId &&
|
|
306
|
+
label.mspConnectionPairIds.length > 0 &&
|
|
307
|
+
label.pinIds.some((pinId) =>
|
|
308
|
+
firstComponent.pinIds.includes(pinId),
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
const secondLabel = this.input.netLabelPlacements.find(
|
|
312
|
+
(label) =>
|
|
313
|
+
label.globalConnNetId === globalConnNetId &&
|
|
314
|
+
label.mspConnectionPairIds.length > 0 &&
|
|
315
|
+
label.pinIds.some((pinId) =>
|
|
316
|
+
secondComponent.pinIds.includes(pinId),
|
|
317
|
+
),
|
|
318
|
+
)
|
|
319
|
+
if (!firstLabel || !secondLabel || firstLabel === secondLabel)
|
|
320
|
+
continue
|
|
321
|
+
if (
|
|
322
|
+
getPerpendicularOffset(
|
|
323
|
+
firstLabel.anchorPoint,
|
|
324
|
+
secondLabel.anchorPoint,
|
|
325
|
+
) > MAX_ROUTED_COMPONENT_RECOVERY_PERPENDICULAR_OFFSET
|
|
326
|
+
) {
|
|
327
|
+
continue
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let bestCandidate: CandidatePair | undefined
|
|
331
|
+
for (const firstPinId of firstComponent.pinIds) {
|
|
332
|
+
for (const secondPinId of secondComponent.pinIds) {
|
|
333
|
+
const firstPin = this.pinMap.get(firstPinId)
|
|
334
|
+
const secondPin = this.pinMap.get(secondPinId)
|
|
335
|
+
if (!firstPin || !secondPin) continue
|
|
336
|
+
const perpendicularOffset = getPerpendicularOffset(
|
|
337
|
+
firstPin,
|
|
338
|
+
secondPin,
|
|
339
|
+
)
|
|
340
|
+
if (
|
|
341
|
+
perpendicularOffset >
|
|
342
|
+
MAX_ROUTED_COMPONENT_RECOVERY_PERPENDICULAR_OFFSET ||
|
|
343
|
+
arePinsCoFacingAlongSeparationAxis(firstPin, secondPin) ||
|
|
344
|
+
arePinsInDifferentSchematicSections(
|
|
345
|
+
this.inputProblem,
|
|
346
|
+
firstPin,
|
|
347
|
+
secondPin,
|
|
348
|
+
) ||
|
|
349
|
+
doesPairCrossRestrictedCenterLines({
|
|
350
|
+
inputProblem: this.inputProblem,
|
|
351
|
+
chipMap: this.chipMap,
|
|
352
|
+
pinIdMap: this.pinMap,
|
|
353
|
+
p1: firstPin,
|
|
354
|
+
p2: secondPin,
|
|
355
|
+
})
|
|
356
|
+
) {
|
|
357
|
+
continue
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const routeDistance =
|
|
361
|
+
Math.abs(firstPin.x - secondPin.x) +
|
|
362
|
+
Math.abs(firstPin.y - secondPin.y)
|
|
363
|
+
const candidate: CandidatePair = {
|
|
364
|
+
firstLabel,
|
|
365
|
+
secondLabel,
|
|
366
|
+
pins: [firstPin, secondPin],
|
|
367
|
+
perpendicularOffset,
|
|
368
|
+
routeDistance,
|
|
369
|
+
key: getCanonicalPairKey(firstPin.pinId, secondPin.pinId),
|
|
370
|
+
recoveryMode: "routed_components",
|
|
371
|
+
netConnectionPinIds: connection.pinIds,
|
|
372
|
+
}
|
|
373
|
+
if (
|
|
374
|
+
!bestCandidate ||
|
|
375
|
+
candidate.routeDistance < bestCandidate.routeDistance ||
|
|
376
|
+
(candidate.routeDistance === bestCandidate.routeDistance &&
|
|
377
|
+
(candidate.perpendicularOffset <
|
|
378
|
+
bestCandidate.perpendicularOffset ||
|
|
379
|
+
(candidate.perpendicularOffset ===
|
|
380
|
+
bestCandidate.perpendicularOffset &&
|
|
381
|
+
candidate.key.localeCompare(bestCandidate.key) < 0)))
|
|
382
|
+
) {
|
|
383
|
+
bestCandidate = candidate
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (bestCandidate) candidates.push(bestCandidate)
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return candidates
|
|
393
|
+
}
|
|
394
|
+
|
|
216
395
|
private isSupersededConnectorTrace(
|
|
217
396
|
trace: SolvedTracePath,
|
|
218
397
|
candidate: CandidatePair,
|
|
@@ -247,8 +426,15 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
247
426
|
const retainedTraces = this.outputTraces.filter(
|
|
248
427
|
(trace) => !this.isSupersededConnectorTrace(trace, candidate),
|
|
249
428
|
)
|
|
429
|
+
let collisionTraces = retainedTraces
|
|
430
|
+
if (candidate.recoveryMode === "routed_components") {
|
|
431
|
+
collisionTraces = retainedTraces.filter(
|
|
432
|
+
(trace) =>
|
|
433
|
+
trace.globalConnNetId !== candidate.firstLabel.globalConnNetId,
|
|
434
|
+
)
|
|
435
|
+
}
|
|
250
436
|
if (
|
|
251
|
-
doesTraceOverlapWithExistingTraces(tracePath,
|
|
437
|
+
doesTraceOverlapWithExistingTraces(tracePath, collisionTraces) ||
|
|
252
438
|
this.routeIntersectsRemainingLabels(tracePath, candidate)
|
|
253
439
|
) {
|
|
254
440
|
return
|
|
@@ -267,13 +453,27 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
267
453
|
}
|
|
268
454
|
|
|
269
455
|
this.outputTraces = [...retainedTraces, recoveredTrace]
|
|
270
|
-
|
|
271
|
-
(
|
|
272
|
-
label
|
|
273
|
-
|
|
456
|
+
if (candidate.recoveryMode === "fallback_labels") {
|
|
457
|
+
this.outputNetLabelPlacements = this.outputNetLabelPlacements.filter(
|
|
458
|
+
(label) =>
|
|
459
|
+
label !== candidate.firstLabel && label !== candidate.secondLabel,
|
|
460
|
+
)
|
|
461
|
+
}
|
|
274
462
|
this.stats.recoveredTraceCount++
|
|
275
463
|
}
|
|
276
464
|
|
|
465
|
+
private areCandidatePinsAlreadyConnected(candidate: CandidatePair) {
|
|
466
|
+
if (!candidate.netConnectionPinIds) return false
|
|
467
|
+
return getTraceConnectedPinComponents({
|
|
468
|
+
pinIds: candidate.netConnectionPinIds,
|
|
469
|
+
traces: this.outputTraces,
|
|
470
|
+
}).some(
|
|
471
|
+
(component) =>
|
|
472
|
+
component.pinIds.includes(candidate.pins[0].pinId) &&
|
|
473
|
+
component.pinIds.includes(candidate.pins[1].pinId),
|
|
474
|
+
)
|
|
475
|
+
}
|
|
476
|
+
|
|
277
477
|
override _step() {
|
|
278
478
|
if (this.activeSubSolver) {
|
|
279
479
|
this.activeSubSolver.step()
|
|
@@ -292,7 +492,8 @@ export class NetLabelToTraceSolver extends BaseSolver {
|
|
|
292
492
|
while (
|
|
293
493
|
candidate &&
|
|
294
494
|
(!this.outputNetLabelPlacements.includes(candidate.firstLabel) ||
|
|
295
|
-
!this.outputNetLabelPlacements.includes(candidate.secondLabel)
|
|
495
|
+
!this.outputNetLabelPlacements.includes(candidate.secondLabel) ||
|
|
496
|
+
this.areCandidatePinsAlreadyConnected(candidate))
|
|
296
497
|
) {
|
|
297
498
|
candidate = this.queuedCandidates.shift()
|
|
298
499
|
}
|
package/package.json
CHANGED
|
@@ -2589,6 +2589,7 @@
|
|
|
2589
2589
|
"netConnections": [
|
|
2590
2590
|
{
|
|
2591
2591
|
"netId": "NET_01",
|
|
2592
|
+
"isGround": false,
|
|
2592
2593
|
"netLabelWidth": 0.42,
|
|
2593
2594
|
"netLabelHeight": 0.84,
|
|
2594
2595
|
"pinIds": [
|
|
@@ -2649,6 +2650,7 @@
|
|
|
2649
2650
|
},
|
|
2650
2651
|
{
|
|
2651
2652
|
"netId": "NET_08",
|
|
2653
|
+
"isGround": false,
|
|
2652
2654
|
"netLabelWidth": 0.42,
|
|
2653
2655
|
"netLabelHeight": 0.84,
|
|
2654
2656
|
"pinIds": [
|
|
@@ -2666,6 +2668,7 @@
|
|
|
2666
2668
|
},
|
|
2667
2669
|
{
|
|
2668
2670
|
"netId": "BAT",
|
|
2671
|
+
"isGround": false,
|
|
2669
2672
|
"netLabelWidth": 0.42,
|
|
2670
2673
|
"netLabelHeight": 0.48,
|
|
2671
2674
|
"pinIds": [
|
|
@@ -2678,6 +2681,7 @@
|
|
|
2678
2681
|
},
|
|
2679
2682
|
{
|
|
2680
2683
|
"netId": "NET_03",
|
|
2684
|
+
"isGround": false,
|
|
2681
2685
|
"netLabelWidth": 0.42,
|
|
2682
2686
|
"netLabelHeight": 0.84,
|
|
2683
2687
|
"pinIds": [
|
|
@@ -2690,6 +2694,7 @@
|
|
|
2690
2694
|
},
|
|
2691
2695
|
{
|
|
2692
2696
|
"netId": "NET_05",
|
|
2697
|
+
"isGround": false,
|
|
2693
2698
|
"netLabelWidth": 0.42,
|
|
2694
2699
|
"netLabelHeight": 0.84,
|
|
2695
2700
|
"pinIds": [
|
|
@@ -2725,6 +2730,7 @@
|
|
|
2725
2730
|
},
|
|
2726
2731
|
{
|
|
2727
2732
|
"netId": "PPHV",
|
|
2733
|
+
"isGround": false,
|
|
2728
2734
|
"netLabelWidth": 0.42,
|
|
2729
2735
|
"netLabelHeight": 0.6,
|
|
2730
2736
|
"pinIds": [
|