@tscircuit/schematic-trace-solver 0.0.165 → 0.0.167
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 +243 -8
- package/lib/solvers/MspConnectionPairSolver/getGroundConnectionPolicy.ts +38 -4
- package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +11 -5
- package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +1 -1
- package/lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts +310 -0
- package/lib/types/InputProblem.ts +5 -1
- package/package.json +1 -1
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
- package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-isolated-rs485-isow7841.snap.svg +10 -10
- package/tests/repros/__snapshots__/repro-nrf52810-clock-routing.snap.svg +18 -18
- package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +44 -36
- package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +18 -18
- package/tests/repros/repro-board-648-esp12f-section.test.ts +3 -0
- package/tests/repros/repro-isolated-rs485-isow7841.test.ts +33 -1
- package/tests/repros/repro-nrf52810-clock-routing.test.ts +26 -1
- package/tests/repros/repro-pga300-redundant-ground-traces.test.ts +35 -7
- package/tests/solvers/MspConnectionPairSolver/ground-direct-connection-groups.test.ts +120 -0
- package/tests/solvers/SameNetJunctionAlignmentSolver/collapse-same-net-cycles.test.ts +154 -0
package/dist/index.d.ts
CHANGED
|
@@ -141,7 +141,11 @@ interface InputDirectConnection {
|
|
|
141
141
|
interface InputNetConnection {
|
|
142
142
|
netId: string;
|
|
143
143
|
pinIds: Array<PinId>;
|
|
144
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* Preserve explicitly wired islands on this ground net, joining the islands
|
|
146
|
+
* electrically through labels instead of routing between them. Nets without
|
|
147
|
+
* explicit traces, or without this hint, retain automatic bus routing.
|
|
148
|
+
*/
|
|
145
149
|
isGround?: boolean;
|
|
146
150
|
/**
|
|
147
151
|
* User-facing text to render for this net label. `netId` remains the stable
|
package/dist/index.js
CHANGED
|
@@ -369,10 +369,17 @@ var MAX_LOCAL_GROUND_BRANCH_OFFSET = 1;
|
|
|
369
369
|
var getGroundConnectionPolicy = (inputProblem) => {
|
|
370
370
|
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
|
|
371
371
|
const groundNetId = netConnMap.getNetConnectedToId("GND");
|
|
372
|
-
if (!groundNetId) return () => true;
|
|
373
372
|
const physicalConnMap = new ConnectivityMap2({});
|
|
373
|
+
const explicitlyWiredGroundNetIds = /* @__PURE__ */ new Set();
|
|
374
374
|
for (const connection of inputProblem.directConnections) {
|
|
375
375
|
physicalConnMap.addConnections([connection.pinIds]);
|
|
376
|
+
const globalNetId = netConnMap.getNetConnectedToId(connection.pinIds[0]);
|
|
377
|
+
const isMarkedGround = inputProblem.netConnections.some(
|
|
378
|
+
(net) => net.isGround && netConnMap.getNetConnectedToId(net.netId) === globalNetId
|
|
379
|
+
);
|
|
380
|
+
if (globalNetId && isMarkedGround) {
|
|
381
|
+
explicitlyWiredGroundNetIds.add(globalNetId);
|
|
382
|
+
}
|
|
376
383
|
}
|
|
377
384
|
const pins = new Map(
|
|
378
385
|
inputProblem.chips.flatMap(
|
|
@@ -388,13 +395,28 @@ var getGroundConnectionPolicy = (inputProblem) => {
|
|
|
388
395
|
({ pin: b }) => a.pinId !== b.pinId && Math.abs(a.y - b.y) < 1e-6 && Math.abs(a.x - b.x) > 1e-6 && physicalConnMap.getNetConnectedToId(b.pinId) === group
|
|
389
396
|
);
|
|
390
397
|
});
|
|
391
|
-
|
|
398
|
+
const areSeparateExplicitGroundIslands = (firstPinId, secondPinId) => {
|
|
399
|
+
const firstGlobalNetId = netConnMap.getNetConnectedToId(firstPinId);
|
|
400
|
+
const secondGlobalNetId = netConnMap.getNetConnectedToId(secondPinId);
|
|
401
|
+
if (!firstGlobalNetId || firstGlobalNetId !== secondGlobalNetId || !explicitlyWiredGroundNetIds.has(firstGlobalNetId)) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
const firstPhysicalGroup = physicalConnMap.getNetConnectedToId(firstPinId) ?? `pin:${firstPinId}`;
|
|
405
|
+
const secondPhysicalGroup = physicalConnMap.getNetConnectedToId(secondPinId) ?? `pin:${secondPinId}`;
|
|
406
|
+
return firstPhysicalGroup !== secondPhysicalGroup;
|
|
407
|
+
};
|
|
408
|
+
if (!hasExplicitParallelGroundRail) {
|
|
409
|
+
return (firstPinId, secondPinId) => !areSeparateExplicitGroundIslands(firstPinId, secondPinId);
|
|
410
|
+
}
|
|
392
411
|
const isGroundFacingTerminal = (pinId) => {
|
|
393
412
|
const entry = pins.get(pinId);
|
|
394
413
|
return entry?.chip.pins.length === 2 && !physicalConnMap.getNetConnectedToId(pinId) && (entry.pin._facingDirection ?? getPinDirection(entry.pin, entry.chip)) === "y-";
|
|
395
414
|
};
|
|
396
415
|
return (firstPinId, secondPinId) => {
|
|
397
|
-
|
|
416
|
+
const firstGlobalNetId = netConnMap.getNetConnectedToId(firstPinId);
|
|
417
|
+
const secondGlobalNetId = netConnMap.getNetConnectedToId(secondPinId);
|
|
418
|
+
if (areSeparateExplicitGroundIslands(firstPinId, secondPinId)) return false;
|
|
419
|
+
if (!groundNetId || firstGlobalNetId !== groundNetId || secondGlobalNetId !== groundNetId) {
|
|
398
420
|
return true;
|
|
399
421
|
}
|
|
400
422
|
if (!isGroundFacingTerminal(firstPinId) && !isGroundFacingTerminal(secondPinId))
|
|
@@ -13009,6 +13031,214 @@ var alignSameNetJunctions = ({
|
|
|
13009
13031
|
};
|
|
13010
13032
|
};
|
|
13011
13033
|
|
|
13034
|
+
// lib/solvers/SameNetJunctionAlignmentSolver/collapseSameNetCycles.ts
|
|
13035
|
+
import { getSegmentIntersection as getSegmentIntersection4 } from "@tscircuit/math-utils/line-intersections";
|
|
13036
|
+
var TRACE_LENGTH_EPSILON = 1e-6;
|
|
13037
|
+
var getTracePathStartingAtPin = (trace, pin) => {
|
|
13038
|
+
const pathStart = trace.tracePath[0];
|
|
13039
|
+
const pathEnd = trace.tracePath.at(-1);
|
|
13040
|
+
if (!pathStart || !pathEnd) return null;
|
|
13041
|
+
if (pointsEqual(pathStart, pin)) return trace.tracePath;
|
|
13042
|
+
if (pointsEqual(pathEnd, pin)) return [...trace.tracePath].reverse();
|
|
13043
|
+
return null;
|
|
13044
|
+
};
|
|
13045
|
+
var getPerpendicularPathCrossings = (targetPath, donorPath) => {
|
|
13046
|
+
const crossings = [];
|
|
13047
|
+
const sharedEndpoint = targetPath[0];
|
|
13048
|
+
for (let targetSegmentIndex = 0; targetSegmentIndex < targetPath.length - 1; targetSegmentIndex++) {
|
|
13049
|
+
const targetStart = targetPath[targetSegmentIndex];
|
|
13050
|
+
const targetEnd = targetPath[targetSegmentIndex + 1];
|
|
13051
|
+
const targetOrientation = getRailOrientation(targetStart, targetEnd);
|
|
13052
|
+
if (!targetOrientation) continue;
|
|
13053
|
+
for (let donorSegmentIndex = 0; donorSegmentIndex < donorPath.length - 1; donorSegmentIndex++) {
|
|
13054
|
+
const donorStart = donorPath[donorSegmentIndex];
|
|
13055
|
+
const donorEnd = donorPath[donorSegmentIndex + 1];
|
|
13056
|
+
const donorOrientation = getRailOrientation(donorStart, donorEnd);
|
|
13057
|
+
if (!donorOrientation || donorOrientation === targetOrientation) continue;
|
|
13058
|
+
const intersectionPoint = getSegmentIntersection4(
|
|
13059
|
+
targetStart,
|
|
13060
|
+
targetEnd,
|
|
13061
|
+
donorStart,
|
|
13062
|
+
donorEnd
|
|
13063
|
+
);
|
|
13064
|
+
if (!intersectionPoint || pointsEqual(intersectionPoint, sharedEndpoint)) {
|
|
13065
|
+
continue;
|
|
13066
|
+
}
|
|
13067
|
+
crossings.push({
|
|
13068
|
+
intersectionPoint,
|
|
13069
|
+
targetSegmentIndex,
|
|
13070
|
+
donorSegmentIndex
|
|
13071
|
+
});
|
|
13072
|
+
}
|
|
13073
|
+
}
|
|
13074
|
+
return crossings;
|
|
13075
|
+
};
|
|
13076
|
+
var buildCollapsedCyclePath = ({
|
|
13077
|
+
targetTrace,
|
|
13078
|
+
targetPath,
|
|
13079
|
+
donorPath,
|
|
13080
|
+
crossing
|
|
13081
|
+
}) => {
|
|
13082
|
+
const pathFromSharedPin = simplifyPath([
|
|
13083
|
+
...donorPath.slice(0, crossing.donorSegmentIndex + 1),
|
|
13084
|
+
crossing.intersectionPoint,
|
|
13085
|
+
...targetPath.slice(crossing.targetSegmentIndex + 1)
|
|
13086
|
+
]);
|
|
13087
|
+
if (pointsEqual(targetTrace.tracePath[0], targetPath[0])) {
|
|
13088
|
+
return pathFromSharedPin;
|
|
13089
|
+
}
|
|
13090
|
+
return pathFromSharedPin.reverse();
|
|
13091
|
+
};
|
|
13092
|
+
var getNetLabelPlacementsForCycleCollapse = ({
|
|
13093
|
+
targetTrace,
|
|
13094
|
+
tracePath,
|
|
13095
|
+
netLabelPlacements
|
|
13096
|
+
}) => {
|
|
13097
|
+
const candidateNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
|
|
13098
|
+
trace: targetTrace,
|
|
13099
|
+
originalTracePath: targetTrace.tracePath,
|
|
13100
|
+
reroutedTracePath: tracePath,
|
|
13101
|
+
netLabelPlacements
|
|
13102
|
+
});
|
|
13103
|
+
for (let labelIndex = 0; labelIndex < netLabelPlacements.length; labelIndex++) {
|
|
13104
|
+
const label = netLabelPlacements[labelIndex];
|
|
13105
|
+
if (label.globalConnNetId !== targetTrace.globalConnNetId) continue;
|
|
13106
|
+
if (!tracePathContainsPoint(targetTrace.tracePath, label.anchorPoint)) {
|
|
13107
|
+
continue;
|
|
13108
|
+
}
|
|
13109
|
+
const candidateLabel = candidateNetLabelPlacements[labelIndex];
|
|
13110
|
+
if (!tracePathContainsPoint(tracePath, candidateLabel.anchorPoint)) {
|
|
13111
|
+
return null;
|
|
13112
|
+
}
|
|
13113
|
+
}
|
|
13114
|
+
const otherNetLabelPlacements = candidateNetLabelPlacements.filter(
|
|
13115
|
+
(label) => label.globalConnNetId !== targetTrace.globalConnNetId
|
|
13116
|
+
);
|
|
13117
|
+
if (pathIntersectsAnyNetLabel({
|
|
13118
|
+
path: tracePath,
|
|
13119
|
+
netLabelPlacements: otherNetLabelPlacements
|
|
13120
|
+
})) {
|
|
13121
|
+
return null;
|
|
13122
|
+
}
|
|
13123
|
+
const sameNetLabelPlacements = candidateNetLabelPlacements.filter(
|
|
13124
|
+
(label) => label.globalConnNetId === targetTrace.globalConnNetId
|
|
13125
|
+
);
|
|
13126
|
+
if (pathEntersAnyNetLabel({
|
|
13127
|
+
path: tracePath,
|
|
13128
|
+
netLabelPlacements: sameNetLabelPlacements
|
|
13129
|
+
})) {
|
|
13130
|
+
return null;
|
|
13131
|
+
}
|
|
13132
|
+
return candidateNetLabelPlacements;
|
|
13133
|
+
};
|
|
13134
|
+
var candidateIsBetter = (candidate, bestCandidate) => {
|
|
13135
|
+
if (!bestCandidate) return true;
|
|
13136
|
+
const visibleLengthDelta = candidate.netVisibleLength - bestCandidate.netVisibleLength;
|
|
13137
|
+
if (Math.abs(visibleLengthDelta) > TRACE_LENGTH_EPSILON) {
|
|
13138
|
+
return visibleLengthDelta < 0;
|
|
13139
|
+
}
|
|
13140
|
+
return candidate.netVisibleSegmentCount < bestCandidate.netVisibleSegmentCount;
|
|
13141
|
+
};
|
|
13142
|
+
var getBestCycleCollapseCandidate = ({
|
|
13143
|
+
targetTrace,
|
|
13144
|
+
traces,
|
|
13145
|
+
netLabelPlacements
|
|
13146
|
+
}) => {
|
|
13147
|
+
const sameNetTraces = traces.filter(
|
|
13148
|
+
(trace) => trace.globalConnNetId === targetTrace.globalConnNetId
|
|
13149
|
+
);
|
|
13150
|
+
const baselineNetVisibleLength = getVisibleTraceLength(sameNetTraces);
|
|
13151
|
+
const baselineTargetVisibleLength = getVisibleTraceLength([targetTrace]);
|
|
13152
|
+
let bestCandidate = null;
|
|
13153
|
+
for (const donorTrace of sameNetTraces) {
|
|
13154
|
+
if (donorTrace.mspPairId === targetTrace.mspPairId) continue;
|
|
13155
|
+
const sharedPin = getSharedPin({
|
|
13156
|
+
donorTrace,
|
|
13157
|
+
branchTrace: targetTrace
|
|
13158
|
+
});
|
|
13159
|
+
if (!sharedPin) continue;
|
|
13160
|
+
const targetPath = getTracePathStartingAtPin(targetTrace, sharedPin);
|
|
13161
|
+
const donorPath = getTracePathStartingAtPin(donorTrace, sharedPin);
|
|
13162
|
+
if (!targetPath || !donorPath) continue;
|
|
13163
|
+
const crossings = getPerpendicularPathCrossings(targetPath, donorPath);
|
|
13164
|
+
for (const crossing of crossings) {
|
|
13165
|
+
const tracePath = buildCollapsedCyclePath({
|
|
13166
|
+
targetTrace,
|
|
13167
|
+
targetPath,
|
|
13168
|
+
donorPath,
|
|
13169
|
+
crossing
|
|
13170
|
+
});
|
|
13171
|
+
const candidateTrace = { ...targetTrace, tracePath };
|
|
13172
|
+
const candidateTargetVisibleLength = getVisibleTraceLength([
|
|
13173
|
+
candidateTrace
|
|
13174
|
+
]);
|
|
13175
|
+
if (candidateTargetVisibleLength > baselineTargetVisibleLength + TRACE_LENGTH_EPSILON) {
|
|
13176
|
+
continue;
|
|
13177
|
+
}
|
|
13178
|
+
const candidateNetLabelPlacements = getNetLabelPlacementsForCycleCollapse(
|
|
13179
|
+
{
|
|
13180
|
+
targetTrace,
|
|
13181
|
+
tracePath,
|
|
13182
|
+
netLabelPlacements
|
|
13183
|
+
}
|
|
13184
|
+
);
|
|
13185
|
+
if (!candidateNetLabelPlacements) continue;
|
|
13186
|
+
const candidateNetTraces = sameNetTraces.map((trace) => {
|
|
13187
|
+
if (trace.mspPairId === targetTrace.mspPairId) return candidateTrace;
|
|
13188
|
+
return trace;
|
|
13189
|
+
});
|
|
13190
|
+
const netVisibleLength = getVisibleTraceLength(candidateNetTraces);
|
|
13191
|
+
if (netVisibleLength >= baselineNetVisibleLength - TRACE_LENGTH_EPSILON) {
|
|
13192
|
+
continue;
|
|
13193
|
+
}
|
|
13194
|
+
const netVisibleSegmentCount = getVisibleTraceSegmentCount(candidateNetTraces);
|
|
13195
|
+
const candidate = {
|
|
13196
|
+
tracePath,
|
|
13197
|
+
netLabelPlacements: candidateNetLabelPlacements,
|
|
13198
|
+
netVisibleLength,
|
|
13199
|
+
netVisibleSegmentCount
|
|
13200
|
+
};
|
|
13201
|
+
if (candidateIsBetter(candidate, bestCandidate)) {
|
|
13202
|
+
bestCandidate = candidate;
|
|
13203
|
+
}
|
|
13204
|
+
}
|
|
13205
|
+
}
|
|
13206
|
+
return bestCandidate;
|
|
13207
|
+
};
|
|
13208
|
+
var collapseSameNetCycles = ({
|
|
13209
|
+
traces,
|
|
13210
|
+
netLabelPlacements
|
|
13211
|
+
}) => {
|
|
13212
|
+
const outputTraces = [...traces];
|
|
13213
|
+
let outputNetLabelPlacements = [...netLabelPlacements];
|
|
13214
|
+
let collapsedCycleCount = 0;
|
|
13215
|
+
let traceIndex = 0;
|
|
13216
|
+
while (traceIndex < outputTraces.length) {
|
|
13217
|
+
const targetTrace = outputTraces[traceIndex];
|
|
13218
|
+
const candidate = getBestCycleCollapseCandidate({
|
|
13219
|
+
targetTrace,
|
|
13220
|
+
traces: outputTraces,
|
|
13221
|
+
netLabelPlacements: outputNetLabelPlacements
|
|
13222
|
+
});
|
|
13223
|
+
if (!candidate) {
|
|
13224
|
+
traceIndex++;
|
|
13225
|
+
continue;
|
|
13226
|
+
}
|
|
13227
|
+
outputTraces[traceIndex] = {
|
|
13228
|
+
...targetTrace,
|
|
13229
|
+
tracePath: candidate.tracePath
|
|
13230
|
+
};
|
|
13231
|
+
outputNetLabelPlacements = candidate.netLabelPlacements;
|
|
13232
|
+
collapsedCycleCount++;
|
|
13233
|
+
traceIndex = 0;
|
|
13234
|
+
}
|
|
13235
|
+
return {
|
|
13236
|
+
traces: outputTraces,
|
|
13237
|
+
netLabelPlacements: outputNetLabelPlacements,
|
|
13238
|
+
collapsedCycleCount
|
|
13239
|
+
};
|
|
13240
|
+
};
|
|
13241
|
+
|
|
13012
13242
|
// lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts
|
|
13013
13243
|
var placeGroundRailLabelsAtOuterEnd = ({
|
|
13014
13244
|
inputProblem,
|
|
@@ -13090,14 +13320,19 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
|
|
|
13090
13320
|
this.outputNetLabelPlacements = input.netLabelPlacements;
|
|
13091
13321
|
}
|
|
13092
13322
|
_step() {
|
|
13093
|
-
const
|
|
13094
|
-
|
|
13323
|
+
const alignment = alignSameNetJunctions(this.input);
|
|
13324
|
+
const cycleCollapse = collapseSameNetCycles({
|
|
13325
|
+
traces: alignment.traces,
|
|
13326
|
+
netLabelPlacements: alignment.netLabelPlacements
|
|
13327
|
+
});
|
|
13328
|
+
this.outputTraces = cycleCollapse.traces;
|
|
13095
13329
|
this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
|
|
13096
13330
|
inputProblem: this.input.inputProblem,
|
|
13097
|
-
traces:
|
|
13098
|
-
netLabelPlacements:
|
|
13331
|
+
traces: cycleCollapse.traces,
|
|
13332
|
+
netLabelPlacements: cycleCollapse.netLabelPlacements
|
|
13099
13333
|
});
|
|
13100
|
-
this.stats.alignedJunctionCount =
|
|
13334
|
+
this.stats.alignedJunctionCount = alignment.alignedJunctionCount;
|
|
13335
|
+
this.stats.collapsedCycleCount = cycleCollapse.collapsedCycleCount;
|
|
13101
13336
|
this.solved = true;
|
|
13102
13337
|
}
|
|
13103
13338
|
getOutput() {
|
|
@@ -11,12 +11,21 @@ const MAX_LOCAL_GROUND_BRANCH_OFFSET = 1
|
|
|
11
11
|
export const getGroundConnectionPolicy = (inputProblem: InputProblem) => {
|
|
12
12
|
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem)
|
|
13
13
|
const groundNetId = netConnMap.getNetConnectedToId("GND")
|
|
14
|
-
if (!groundNetId) return () => true
|
|
15
14
|
// Net identifiers do not constitute physical edges: two separate direct
|
|
16
15
|
// connections may have the same netId without requesting a wire between them.
|
|
17
16
|
const physicalConnMap = new ConnectivityMap({})
|
|
17
|
+
const explicitlyWiredGroundNetIds = new Set<string>()
|
|
18
18
|
for (const connection of inputProblem.directConnections) {
|
|
19
19
|
physicalConnMap.addConnections([connection.pinIds])
|
|
20
|
+
const globalNetId = netConnMap.getNetConnectedToId(connection.pinIds[0])
|
|
21
|
+
const isMarkedGround = inputProblem.netConnections.some(
|
|
22
|
+
(net) =>
|
|
23
|
+
net.isGround &&
|
|
24
|
+
netConnMap.getNetConnectedToId(net.netId) === globalNetId,
|
|
25
|
+
)
|
|
26
|
+
if (globalNetId && isMarkedGround) {
|
|
27
|
+
explicitlyWiredGroundNetIds.add(globalNetId)
|
|
28
|
+
}
|
|
20
29
|
}
|
|
21
30
|
const pins = new Map(
|
|
22
31
|
inputProblem.chips.flatMap((chip) =>
|
|
@@ -46,7 +55,29 @@ export const getGroundConnectionPolicy = (inputProblem: InputProblem) => {
|
|
|
46
55
|
)
|
|
47
56
|
)
|
|
48
57
|
})
|
|
49
|
-
|
|
58
|
+
const areSeparateExplicitGroundIslands = (
|
|
59
|
+
firstPinId: PinId,
|
|
60
|
+
secondPinId: PinId,
|
|
61
|
+
) => {
|
|
62
|
+
const firstGlobalNetId = netConnMap.getNetConnectedToId(firstPinId)
|
|
63
|
+
const secondGlobalNetId = netConnMap.getNetConnectedToId(secondPinId)
|
|
64
|
+
if (
|
|
65
|
+
!firstGlobalNetId ||
|
|
66
|
+
firstGlobalNetId !== secondGlobalNetId ||
|
|
67
|
+
!explicitlyWiredGroundNetIds.has(firstGlobalNetId)
|
|
68
|
+
) {
|
|
69
|
+
return false
|
|
70
|
+
}
|
|
71
|
+
const firstPhysicalGroup =
|
|
72
|
+
physicalConnMap.getNetConnectedToId(firstPinId) ?? `pin:${firstPinId}`
|
|
73
|
+
const secondPhysicalGroup =
|
|
74
|
+
physicalConnMap.getNetConnectedToId(secondPinId) ?? `pin:${secondPinId}`
|
|
75
|
+
return firstPhysicalGroup !== secondPhysicalGroup
|
|
76
|
+
}
|
|
77
|
+
if (!hasExplicitParallelGroundRail) {
|
|
78
|
+
return (firstPinId: PinId, secondPinId: PinId) =>
|
|
79
|
+
!areSeparateExplicitGroundIslands(firstPinId, secondPinId)
|
|
80
|
+
}
|
|
50
81
|
const isGroundFacingTerminal = (pinId: PinId) => {
|
|
51
82
|
const entry = pins.get(pinId)
|
|
52
83
|
return (
|
|
@@ -58,10 +89,13 @@ export const getGroundConnectionPolicy = (inputProblem: InputProblem) => {
|
|
|
58
89
|
}
|
|
59
90
|
|
|
60
91
|
return (firstPinId: PinId, secondPinId: PinId): boolean => {
|
|
92
|
+
const firstGlobalNetId = netConnMap.getNetConnectedToId(firstPinId)
|
|
93
|
+
const secondGlobalNetId = netConnMap.getNetConnectedToId(secondPinId)
|
|
94
|
+
if (areSeparateExplicitGroundIslands(firstPinId, secondPinId)) return false
|
|
61
95
|
if (
|
|
62
96
|
!groundNetId ||
|
|
63
|
-
|
|
64
|
-
|
|
97
|
+
firstGlobalNetId !== groundNetId ||
|
|
98
|
+
secondGlobalNetId !== groundNetId
|
|
65
99
|
) {
|
|
66
100
|
return true
|
|
67
101
|
}
|
|
@@ -6,6 +6,7 @@ import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/
|
|
|
6
6
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
7
7
|
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
8
8
|
import { alignSameNetJunctions } from "./alignSameNetJunctions"
|
|
9
|
+
import { collapseSameNetCycles } from "./collapseSameNetCycles"
|
|
9
10
|
import { placeGroundRailLabelsAtOuterEnd } from "./placeGroundRailLabelsAtOuterEnd"
|
|
10
11
|
|
|
11
12
|
interface SameNetJunctionAlignmentSolverInput {
|
|
@@ -28,14 +29,19 @@ export class SameNetJunctionAlignmentSolver extends BaseSolver {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
override _step() {
|
|
31
|
-
const
|
|
32
|
-
|
|
32
|
+
const alignment = alignSameNetJunctions(this.input)
|
|
33
|
+
const cycleCollapse = collapseSameNetCycles({
|
|
34
|
+
traces: alignment.traces,
|
|
35
|
+
netLabelPlacements: alignment.netLabelPlacements,
|
|
36
|
+
})
|
|
37
|
+
this.outputTraces = cycleCollapse.traces
|
|
33
38
|
this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
|
|
34
39
|
inputProblem: this.input.inputProblem,
|
|
35
|
-
traces:
|
|
36
|
-
netLabelPlacements:
|
|
40
|
+
traces: cycleCollapse.traces,
|
|
41
|
+
netLabelPlacements: cycleCollapse.netLabelPlacements,
|
|
37
42
|
})
|
|
38
|
-
this.stats.alignedJunctionCount =
|
|
43
|
+
this.stats.alignedJunctionCount = alignment.alignedJunctionCount
|
|
44
|
+
this.stats.collapsedCycleCount = cycleCollapse.collapsedCycleCount
|
|
39
45
|
this.solved = true
|
|
40
46
|
}
|
|
41
47
|
|