@tscircuit/schematic-trace-solver 0.0.39 → 0.0.41
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 +37 -2
- package/dist/index.js +204 -11
- package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +238 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +31 -9
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +3 -1
- package/lib/utils/does-trace-overlap-with-existing-traces.ts +33 -0
- package/package.json +1 -1
- package/site/examples/example25.page.tsx +4 -0
- package/tests/assets/example25.json +96 -0
- package/tests/examples/__snapshots__/example03.snap.svg +67 -76
- package/tests/examples/__snapshots__/example10.snap.svg +24 -30
- package/tests/examples/__snapshots__/example13.snap.svg +81 -84
- package/tests/examples/__snapshots__/example15.snap.svg +5 -8
- package/tests/examples/__snapshots__/example21.snap.svg +68 -74
- package/tests/examples/__snapshots__/example27.snap.svg +281 -0
- package/tests/examples/__snapshots__/example28.snap.svg +273 -0
- package/tests/examples/example27.test.ts +241 -0
- package/tests/examples/example28.test.ts +13 -0
package/dist/index.d.ts
CHANGED
|
@@ -404,6 +404,35 @@ declare class TraceLabelOverlapAvoidanceSolver extends BaseSolver {
|
|
|
404
404
|
visualize(): GraphicsObject;
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
+
declare class LongDistancePairSolver extends BaseSolver {
|
|
408
|
+
private params;
|
|
409
|
+
solvedLongDistanceTraces: SolvedTracePath[];
|
|
410
|
+
private queuedCandidatePairs;
|
|
411
|
+
private currentCandidatePair;
|
|
412
|
+
private subSolver;
|
|
413
|
+
private chipMap;
|
|
414
|
+
private inputProblem;
|
|
415
|
+
private netConnMap;
|
|
416
|
+
private newlyConnectedPinIds;
|
|
417
|
+
private allSolvedTraces;
|
|
418
|
+
constructor(params: {
|
|
419
|
+
inputProblem: InputProblem;
|
|
420
|
+
alreadySolvedTraces: SolvedTracePath[];
|
|
421
|
+
primaryMspConnectionPairs: MspConnectionPair[];
|
|
422
|
+
});
|
|
423
|
+
getConstructorParams(): {
|
|
424
|
+
inputProblem: InputProblem;
|
|
425
|
+
alreadySolvedTraces: SolvedTracePath[];
|
|
426
|
+
primaryMspConnectionPairs: MspConnectionPair[];
|
|
427
|
+
};
|
|
428
|
+
_step(): void;
|
|
429
|
+
visualize(): graphics_debug.GraphicsObject;
|
|
430
|
+
getOutput(): {
|
|
431
|
+
newTraces: SolvedTracePath[];
|
|
432
|
+
allTracesMerged: SolvedTracePath[];
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
|
|
407
436
|
/**
|
|
408
437
|
* Pipeline solver that runs a series of solvers to find the best schematic layout.
|
|
409
438
|
* Coordinates the entire layout process from chip partitioning through final packing.
|
|
@@ -414,10 +443,16 @@ type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
|
|
|
414
443
|
solverClass: T;
|
|
415
444
|
getConstructorParams: (instance: SchematicTracePipelineSolver) => ConstructorParameters<T>;
|
|
416
445
|
onSolved?: (instance: SchematicTracePipelineSolver) => void;
|
|
446
|
+
shouldSkip?: (instance: SchematicTracePipelineSolver) => boolean;
|
|
417
447
|
};
|
|
448
|
+
interface SchematicTracePipelineSolverParams {
|
|
449
|
+
inputProblem: InputProblem;
|
|
450
|
+
allowLongDistanceTraces?: boolean;
|
|
451
|
+
}
|
|
418
452
|
declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
419
453
|
mspConnectionPairSolver?: MspConnectionPairSolver;
|
|
420
454
|
schematicTraceLinesSolver?: SchematicTraceLinesSolver;
|
|
455
|
+
longDistancePairSolver?: LongDistancePairSolver;
|
|
421
456
|
traceOverlapShiftSolver?: TraceOverlapShiftSolver;
|
|
422
457
|
netLabelPlacementSolver?: NetLabelPlacementSolver;
|
|
423
458
|
traceLabelOverlapAvoidanceSolver?: TraceLabelOverlapAvoidanceSolver;
|
|
@@ -426,7 +461,7 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
426
461
|
timeSpentOnPhase: Record<string, number>;
|
|
427
462
|
firstIterationOfPhase: Record<string, number>;
|
|
428
463
|
inputProblem: InputProblem;
|
|
429
|
-
pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver>)[];
|
|
464
|
+
pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver>)[];
|
|
430
465
|
constructor(inputProblem: InputProblem);
|
|
431
466
|
getConstructorParams(): ConstructorParameters<typeof SchematicTracePipelineSolver>[0];
|
|
432
467
|
currentPipelineStepIndex: number;
|
|
@@ -442,4 +477,4 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
442
477
|
preview(): GraphicsObject;
|
|
443
478
|
}
|
|
444
479
|
|
|
445
|
-
export { type ChipId, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem, type NetId, type PinId, SchematicTracePipelineSolver, SchematicTraceSingleLineSolver2 };
|
|
480
|
+
export { type ChipId, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem, type NetId, type PinId, SchematicTracePipelineSolver, type SchematicTracePipelineSolverParams, SchematicTraceSingleLineSolver2 };
|
package/dist/index.js
CHANGED
|
@@ -3009,7 +3009,9 @@ var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
|
3009
3009
|
)
|
|
3010
3010
|
});
|
|
3011
3011
|
finalLabelPlacementSolver.solve();
|
|
3012
|
-
|
|
3012
|
+
if (!finalLabelPlacementSolver.failed) {
|
|
3013
|
+
this.netLabelPlacements = finalLabelPlacementSolver.netLabelPlacements;
|
|
3014
|
+
}
|
|
3013
3015
|
this.solved = true;
|
|
3014
3016
|
}
|
|
3015
3017
|
getOutput() {
|
|
@@ -3098,19 +3100,203 @@ var expandChipsToFitPins = (problem) => {
|
|
|
3098
3100
|
}
|
|
3099
3101
|
};
|
|
3100
3102
|
|
|
3103
|
+
// lib/utils/does-trace-overlap-with-existing-traces.ts
|
|
3104
|
+
import { doSegmentsIntersect } from "@tscircuit/math-utils";
|
|
3105
|
+
function doesTraceOverlapWithExistingTraces(newTracePath, existingTraces) {
|
|
3106
|
+
for (let i = 0; i < newTracePath.length - 1; i++) {
|
|
3107
|
+
const newSegmentP1 = newTracePath[i];
|
|
3108
|
+
const newSegmentP2 = newTracePath[i + 1];
|
|
3109
|
+
for (const existingTrace of existingTraces) {
|
|
3110
|
+
for (let j = 0; j < existingTrace.tracePath.length - 1; j++) {
|
|
3111
|
+
const existingSegmentP1 = existingTrace.tracePath[j];
|
|
3112
|
+
const existingSegmentP2 = existingTrace.tracePath[j + 1];
|
|
3113
|
+
if (doSegmentsIntersect(
|
|
3114
|
+
newSegmentP1,
|
|
3115
|
+
newSegmentP2,
|
|
3116
|
+
existingSegmentP1,
|
|
3117
|
+
existingSegmentP2
|
|
3118
|
+
)) {
|
|
3119
|
+
return true;
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
return false;
|
|
3125
|
+
}
|
|
3126
|
+
|
|
3127
|
+
// lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts
|
|
3128
|
+
var NEAREST_NEIGHBOR_COUNT = 3;
|
|
3129
|
+
var distance = (p1, p2) => {
|
|
3130
|
+
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
|
|
3131
|
+
};
|
|
3132
|
+
var LongDistancePairSolver = class extends BaseSolver {
|
|
3133
|
+
constructor(params) {
|
|
3134
|
+
super();
|
|
3135
|
+
this.params = params;
|
|
3136
|
+
const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } = this.params;
|
|
3137
|
+
this.inputProblem = inputProblem;
|
|
3138
|
+
this.allSolvedTraces = [...alreadySolvedTraces];
|
|
3139
|
+
const primaryConnectedPinIds = /* @__PURE__ */ new Set();
|
|
3140
|
+
for (const pair of primaryMspConnectionPairs) {
|
|
3141
|
+
primaryConnectedPinIds.add(pair.pins[0].pinId);
|
|
3142
|
+
primaryConnectedPinIds.add(pair.pins[1].pinId);
|
|
3143
|
+
}
|
|
3144
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
|
|
3145
|
+
this.netConnMap = netConnMap;
|
|
3146
|
+
const pinMap = /* @__PURE__ */ new Map();
|
|
3147
|
+
for (const chip of inputProblem.chips) {
|
|
3148
|
+
this.chipMap[chip.chipId] = chip;
|
|
3149
|
+
for (const pin of chip.pins) {
|
|
3150
|
+
pinMap.set(pin.pinId, { ...pin, chipId: chip.chipId });
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
const candidatePairs = [];
|
|
3154
|
+
const addedPairKeys = /* @__PURE__ */ new Set();
|
|
3155
|
+
for (const netId of Object.keys(netConnMap.netMap)) {
|
|
3156
|
+
const allPinIdsInNet = netConnMap.getIdsConnectedToNet(netId);
|
|
3157
|
+
if (allPinIdsInNet.length < 2) continue;
|
|
3158
|
+
const unconnectedPinIds = allPinIdsInNet.filter(
|
|
3159
|
+
(pinId) => !primaryConnectedPinIds.has(pinId)
|
|
3160
|
+
);
|
|
3161
|
+
for (const unconnectedPinId of unconnectedPinIds) {
|
|
3162
|
+
const sourcePin = pinMap.get(unconnectedPinId);
|
|
3163
|
+
if (!sourcePin) continue;
|
|
3164
|
+
const neighbors = allPinIdsInNet.filter((otherPinId) => otherPinId !== unconnectedPinId).flatMap((otherPinId) => {
|
|
3165
|
+
const targetPin = pinMap.get(otherPinId);
|
|
3166
|
+
if (!targetPin) return [];
|
|
3167
|
+
return [
|
|
3168
|
+
{
|
|
3169
|
+
pin: targetPin,
|
|
3170
|
+
distance: distance(sourcePin, targetPin)
|
|
3171
|
+
}
|
|
3172
|
+
];
|
|
3173
|
+
}).sort((a, b) => a.distance - b.distance).slice(0, NEAREST_NEIGHBOR_COUNT);
|
|
3174
|
+
for (const neighbor of neighbors) {
|
|
3175
|
+
const pair = [sourcePin, neighbor.pin];
|
|
3176
|
+
const pairKey = pair.map((p) => p.pinId).sort().join("--");
|
|
3177
|
+
if (!addedPairKeys.has(pairKey)) {
|
|
3178
|
+
candidatePairs.push(pair);
|
|
3179
|
+
addedPairKeys.add(pairKey);
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
}
|
|
3184
|
+
this.queuedCandidatePairs = candidatePairs;
|
|
3185
|
+
}
|
|
3186
|
+
solvedLongDistanceTraces = [];
|
|
3187
|
+
queuedCandidatePairs = [];
|
|
3188
|
+
currentCandidatePair = null;
|
|
3189
|
+
subSolver = null;
|
|
3190
|
+
chipMap = {};
|
|
3191
|
+
inputProblem;
|
|
3192
|
+
netConnMap;
|
|
3193
|
+
newlyConnectedPinIds = /* @__PURE__ */ new Set();
|
|
3194
|
+
allSolvedTraces = [];
|
|
3195
|
+
getConstructorParams() {
|
|
3196
|
+
return this.params;
|
|
3197
|
+
}
|
|
3198
|
+
_step() {
|
|
3199
|
+
if (this.subSolver?.solved) {
|
|
3200
|
+
const newTracePath = this.subSolver.solvedTracePath;
|
|
3201
|
+
if (newTracePath && this.currentCandidatePair) {
|
|
3202
|
+
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
3203
|
+
newTracePath,
|
|
3204
|
+
this.allSolvedTraces
|
|
3205
|
+
);
|
|
3206
|
+
if (isTraceClear) {
|
|
3207
|
+
const [p1, p2] = this.currentCandidatePair;
|
|
3208
|
+
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId);
|
|
3209
|
+
const mspPairId = `${p1.pinId}-${p2.pinId}`;
|
|
3210
|
+
const newSolvedTrace = {
|
|
3211
|
+
mspPairId,
|
|
3212
|
+
dcConnNetId: globalConnNetId,
|
|
3213
|
+
globalConnNetId,
|
|
3214
|
+
pins: [p1, p2],
|
|
3215
|
+
tracePath: newTracePath,
|
|
3216
|
+
mspConnectionPairIds: [mspPairId],
|
|
3217
|
+
pinIds: [p1.pinId, p2.pinId]
|
|
3218
|
+
};
|
|
3219
|
+
this.solvedLongDistanceTraces.push(newSolvedTrace);
|
|
3220
|
+
this.allSolvedTraces.push(newSolvedTrace);
|
|
3221
|
+
this.newlyConnectedPinIds.add(p1.pinId);
|
|
3222
|
+
this.newlyConnectedPinIds.add(p2.pinId);
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
this.subSolver = null;
|
|
3226
|
+
this.currentCandidatePair = null;
|
|
3227
|
+
} else if (this.subSolver?.failed) {
|
|
3228
|
+
this.subSolver = null;
|
|
3229
|
+
this.currentCandidatePair = null;
|
|
3230
|
+
}
|
|
3231
|
+
if (this.subSolver) {
|
|
3232
|
+
this.subSolver.step();
|
|
3233
|
+
return;
|
|
3234
|
+
}
|
|
3235
|
+
while (this.queuedCandidatePairs.length > 0) {
|
|
3236
|
+
const nextPair = this.queuedCandidatePairs.shift();
|
|
3237
|
+
const [p1, p2] = nextPair;
|
|
3238
|
+
if (this.newlyConnectedPinIds.has(p1.pinId) || this.newlyConnectedPinIds.has(p2.pinId)) {
|
|
3239
|
+
continue;
|
|
3240
|
+
}
|
|
3241
|
+
this.currentCandidatePair = nextPair;
|
|
3242
|
+
this.subSolver = new SchematicTraceSingleLineSolver2({
|
|
3243
|
+
inputProblem: this.params.inputProblem,
|
|
3244
|
+
pins: this.currentCandidatePair,
|
|
3245
|
+
chipMap: this.chipMap
|
|
3246
|
+
});
|
|
3247
|
+
return;
|
|
3248
|
+
}
|
|
3249
|
+
this.solved = true;
|
|
3250
|
+
}
|
|
3251
|
+
visualize() {
|
|
3252
|
+
if (this.subSolver) {
|
|
3253
|
+
return this.subSolver.visualize();
|
|
3254
|
+
}
|
|
3255
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
3256
|
+
for (const trace of this.solvedLongDistanceTraces) {
|
|
3257
|
+
graphics.lines.push({
|
|
3258
|
+
points: trace.tracePath,
|
|
3259
|
+
strokeColor: "purple"
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
3262
|
+
for (const [p1, p2] of this.queuedCandidatePairs) {
|
|
3263
|
+
graphics.lines.push({
|
|
3264
|
+
points: [p1, p2],
|
|
3265
|
+
strokeColor: "gray",
|
|
3266
|
+
strokeDash: "4 4"
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3269
|
+
return graphics;
|
|
3270
|
+
}
|
|
3271
|
+
getOutput() {
|
|
3272
|
+
if (!this.solved) {
|
|
3273
|
+
return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces };
|
|
3274
|
+
}
|
|
3275
|
+
return {
|
|
3276
|
+
newTraces: this.solvedLongDistanceTraces,
|
|
3277
|
+
allTracesMerged: [
|
|
3278
|
+
...this.params.alreadySolvedTraces,
|
|
3279
|
+
...this.solvedLongDistanceTraces
|
|
3280
|
+
]
|
|
3281
|
+
};
|
|
3282
|
+
}
|
|
3283
|
+
};
|
|
3284
|
+
|
|
3101
3285
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
3102
3286
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
3103
3287
|
return {
|
|
3104
3288
|
solverName,
|
|
3105
3289
|
solverClass,
|
|
3106
3290
|
getConstructorParams,
|
|
3107
|
-
onSolved: opts.onSolved
|
|
3291
|
+
onSolved: opts.onSolved,
|
|
3292
|
+
shouldSkip: opts.shouldSkip
|
|
3108
3293
|
};
|
|
3109
3294
|
}
|
|
3110
3295
|
var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
3111
3296
|
mspConnectionPairSolver;
|
|
3112
3297
|
// guidelinesSolver?: GuidelinesSolver
|
|
3113
3298
|
schematicTraceLinesSolver;
|
|
3299
|
+
longDistancePairSolver;
|
|
3114
3300
|
traceOverlapShiftSolver;
|
|
3115
3301
|
netLabelPlacementSolver;
|
|
3116
3302
|
traceLabelOverlapAvoidanceSolver;
|
|
@@ -3153,6 +3339,17 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3153
3339
|
// guidelines: this.guidelinesSolver!.guidelines,
|
|
3154
3340
|
chipMap: this.mspConnectionPairSolver.chipMap
|
|
3155
3341
|
}
|
|
3342
|
+
]
|
|
3343
|
+
),
|
|
3344
|
+
definePipelineStep(
|
|
3345
|
+
"longDistancePairSolver",
|
|
3346
|
+
LongDistancePairSolver,
|
|
3347
|
+
(instance) => [
|
|
3348
|
+
{
|
|
3349
|
+
inputProblem: instance.inputProblem,
|
|
3350
|
+
primaryMspConnectionPairs: instance.mspConnectionPairSolver.mspConnectionPairs,
|
|
3351
|
+
alreadySolvedTraces: instance.schematicTraceLinesSolver.solvedTracePaths
|
|
3352
|
+
}
|
|
3156
3353
|
],
|
|
3157
3354
|
{
|
|
3158
3355
|
onSolved: (schematicTraceLinesSolver) => {
|
|
@@ -3165,7 +3362,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3165
3362
|
() => [
|
|
3166
3363
|
{
|
|
3167
3364
|
inputProblem: this.inputProblem,
|
|
3168
|
-
inputTracePaths: this.
|
|
3365
|
+
inputTracePaths: this.longDistancePairSolver?.getOutput().allTracesMerged,
|
|
3169
3366
|
globalConnMap: this.mspConnectionPairSolver.globalConnMap
|
|
3170
3367
|
}
|
|
3171
3368
|
],
|
|
@@ -3181,10 +3378,9 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3181
3378
|
{
|
|
3182
3379
|
inputProblem: this.inputProblem,
|
|
3183
3380
|
inputTraceMap: this.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
|
|
3184
|
-
this.
|
|
3185
|
-
p.mspPairId,
|
|
3186
|
-
|
|
3187
|
-
])
|
|
3381
|
+
this.longDistancePairSolver.getOutput().allTracesMerged.map(
|
|
3382
|
+
(p) => [p.mspPairId, p]
|
|
3383
|
+
)
|
|
3188
3384
|
)
|
|
3189
3385
|
}
|
|
3190
3386
|
],
|
|
@@ -3198,10 +3394,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
3198
3394
|
TraceLabelOverlapAvoidanceSolver,
|
|
3199
3395
|
(instance) => {
|
|
3200
3396
|
const traceMap = instance.traceOverlapShiftSolver?.correctedTraceMap ?? Object.fromEntries(
|
|
3201
|
-
instance.
|
|
3202
|
-
p.mspPairId,
|
|
3203
|
-
p
|
|
3204
|
-
])
|
|
3397
|
+
instance.longDistancePairSolver.getOutput().allTracesMerged.map((p) => [p.mspPairId, p])
|
|
3205
3398
|
);
|
|
3206
3399
|
const traces = Object.values(traceMap);
|
|
3207
3400
|
const netLabelPlacements = instance.netLabelPlacementSolver.netLabelPlacements;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
2
|
+
import type { MspConnectionPair } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
|
|
3
|
+
import type {
|
|
4
|
+
InputProblem,
|
|
5
|
+
InputPin,
|
|
6
|
+
PinId,
|
|
7
|
+
InputChip,
|
|
8
|
+
} from "lib/types/InputProblem"
|
|
9
|
+
import { BaseSolver } from "../BaseSolver/BaseSolver"
|
|
10
|
+
import { SchematicTraceSingleLineSolver2 } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
|
|
11
|
+
import { doesTraceOverlapWithExistingTraces } from "lib/utils/does-trace-overlap-with-existing-traces"
|
|
12
|
+
import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"
|
|
13
|
+
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
14
|
+
import type { ConnectivityMap } from "connectivity-map"
|
|
15
|
+
|
|
16
|
+
const NEAREST_NEIGHBOR_COUNT = 3
|
|
17
|
+
|
|
18
|
+
const distance = (p1: InputPin, p2: InputPin) => {
|
|
19
|
+
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2))
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class LongDistancePairSolver extends BaseSolver {
|
|
23
|
+
public solvedLongDistanceTraces: SolvedTracePath[] = []
|
|
24
|
+
private queuedCandidatePairs: Array<
|
|
25
|
+
[InputPin & { chipId: string }, InputPin & { chipId: string }]
|
|
26
|
+
> = []
|
|
27
|
+
private currentCandidatePair:
|
|
28
|
+
| [InputPin & { chipId: string }, InputPin & { chipId: string }]
|
|
29
|
+
| null = null
|
|
30
|
+
private subSolver: SchematicTraceSingleLineSolver2 | null = null
|
|
31
|
+
private chipMap: Record<string, InputChip> = {}
|
|
32
|
+
private inputProblem: InputProblem
|
|
33
|
+
private netConnMap: ConnectivityMap
|
|
34
|
+
private newlyConnectedPinIds = new Set<PinId>()
|
|
35
|
+
private allSolvedTraces: SolvedTracePath[] = []
|
|
36
|
+
|
|
37
|
+
constructor(
|
|
38
|
+
private params: {
|
|
39
|
+
inputProblem: InputProblem
|
|
40
|
+
alreadySolvedTraces: SolvedTracePath[]
|
|
41
|
+
primaryMspConnectionPairs: MspConnectionPair[]
|
|
42
|
+
},
|
|
43
|
+
) {
|
|
44
|
+
super()
|
|
45
|
+
|
|
46
|
+
const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } =
|
|
47
|
+
this.params
|
|
48
|
+
|
|
49
|
+
this.inputProblem = inputProblem
|
|
50
|
+
this.allSolvedTraces = [...alreadySolvedTraces]
|
|
51
|
+
|
|
52
|
+
// 1. Create initial maps and sets for efficient lookup
|
|
53
|
+
const primaryConnectedPinIds = new Set<PinId>()
|
|
54
|
+
for (const pair of primaryMspConnectionPairs) {
|
|
55
|
+
primaryConnectedPinIds.add(pair.pins[0].pinId)
|
|
56
|
+
primaryConnectedPinIds.add(pair.pins[1].pinId)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem)
|
|
60
|
+
this.netConnMap = netConnMap
|
|
61
|
+
const pinMap = new Map<PinId, InputPin & { chipId: string }>()
|
|
62
|
+
for (const chip of inputProblem.chips) {
|
|
63
|
+
this.chipMap[chip.chipId] = chip
|
|
64
|
+
for (const pin of chip.pins) {
|
|
65
|
+
pinMap.set(pin.pinId, { ...pin, chipId: chip.chipId })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 2. Generate candidate pairs using N-Nearest-Neighbors approach
|
|
70
|
+
const candidatePairs: Array<
|
|
71
|
+
[InputPin & { chipId: string }, InputPin & { chipId: string }]
|
|
72
|
+
> = []
|
|
73
|
+
const addedPairKeys = new Set<string>()
|
|
74
|
+
|
|
75
|
+
for (const netId of Object.keys(netConnMap.netMap)) {
|
|
76
|
+
const allPinIdsInNet = netConnMap.getIdsConnectedToNet(netId)
|
|
77
|
+
if (allPinIdsInNet.length < 2) continue
|
|
78
|
+
|
|
79
|
+
const unconnectedPinIds = allPinIdsInNet.filter(
|
|
80
|
+
(pinId) => !primaryConnectedPinIds.has(pinId),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
for (const unconnectedPinId of unconnectedPinIds) {
|
|
84
|
+
const sourcePin = pinMap.get(unconnectedPinId)
|
|
85
|
+
if (!sourcePin) continue
|
|
86
|
+
|
|
87
|
+
const neighbors = allPinIdsInNet
|
|
88
|
+
.filter((otherPinId) => otherPinId !== unconnectedPinId)
|
|
89
|
+
.flatMap((otherPinId) => {
|
|
90
|
+
const targetPin = pinMap.get(otherPinId)
|
|
91
|
+
if (!targetPin) return [] // Gracefully handle missing pins
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
pin: targetPin,
|
|
95
|
+
distance: distance(sourcePin, targetPin),
|
|
96
|
+
},
|
|
97
|
+
]
|
|
98
|
+
})
|
|
99
|
+
.sort((a, b) => a.distance - b.distance)
|
|
100
|
+
.slice(0, NEAREST_NEIGHBOR_COUNT)
|
|
101
|
+
|
|
102
|
+
for (const neighbor of neighbors) {
|
|
103
|
+
const pair: [
|
|
104
|
+
InputPin & { chipId: string },
|
|
105
|
+
InputPin & { chipId: string },
|
|
106
|
+
] = [sourcePin, neighbor.pin]
|
|
107
|
+
const pairKey = pair
|
|
108
|
+
.map((p) => p.pinId)
|
|
109
|
+
.sort()
|
|
110
|
+
.join("--")
|
|
111
|
+
|
|
112
|
+
if (!addedPairKeys.has(pairKey)) {
|
|
113
|
+
candidatePairs.push(pair)
|
|
114
|
+
addedPairKeys.add(pairKey)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
this.queuedCandidatePairs = candidatePairs
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
override getConstructorParams() {
|
|
123
|
+
return this.params
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
override _step() {
|
|
127
|
+
// 1. Check if a sub-solver has finished and process its result
|
|
128
|
+
if (this.subSolver?.solved) {
|
|
129
|
+
const newTracePath = this.subSolver.solvedTracePath
|
|
130
|
+
if (newTracePath && this.currentCandidatePair) {
|
|
131
|
+
const isTraceClear = !doesTraceOverlapWithExistingTraces(
|
|
132
|
+
newTracePath,
|
|
133
|
+
this.allSolvedTraces,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if (isTraceClear) {
|
|
137
|
+
const [p1, p2] = this.currentCandidatePair
|
|
138
|
+
const globalConnNetId = this.netConnMap.getNetConnectedToId(p1.pinId)!
|
|
139
|
+
const mspPairId = `${p1.pinId}-${p2.pinId}`
|
|
140
|
+
|
|
141
|
+
const newSolvedTrace: SolvedTracePath = {
|
|
142
|
+
mspPairId,
|
|
143
|
+
dcConnNetId: globalConnNetId,
|
|
144
|
+
globalConnNetId,
|
|
145
|
+
pins: [p1, p2],
|
|
146
|
+
tracePath: newTracePath,
|
|
147
|
+
mspConnectionPairIds: [mspPairId],
|
|
148
|
+
pinIds: [p1.pinId, p2.pinId],
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
this.solvedLongDistanceTraces.push(newSolvedTrace)
|
|
152
|
+
this.allSolvedTraces.push(newSolvedTrace)
|
|
153
|
+
|
|
154
|
+
this.newlyConnectedPinIds.add(p1.pinId)
|
|
155
|
+
this.newlyConnectedPinIds.add(p2.pinId)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
this.subSolver = null
|
|
159
|
+
this.currentCandidatePair = null
|
|
160
|
+
} else if (this.subSolver?.failed) {
|
|
161
|
+
this.subSolver = null
|
|
162
|
+
this.currentCandidatePair = null
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// 2. If a sub-solver is already running, let it continue
|
|
166
|
+
if (this.subSolver) {
|
|
167
|
+
this.subSolver.step()
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 3. Find the next valid candidate pair and start a new sub-solver
|
|
172
|
+
while (this.queuedCandidatePairs.length > 0) {
|
|
173
|
+
const nextPair = this.queuedCandidatePairs.shift()!
|
|
174
|
+
const [p1, p2] = nextPair
|
|
175
|
+
|
|
176
|
+
if (
|
|
177
|
+
this.newlyConnectedPinIds.has(p1.pinId) ||
|
|
178
|
+
this.newlyConnectedPinIds.has(p2.pinId)
|
|
179
|
+
) {
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
this.currentCandidatePair = nextPair
|
|
184
|
+
this.subSolver = new SchematicTraceSingleLineSolver2({
|
|
185
|
+
inputProblem: this.params.inputProblem,
|
|
186
|
+
pins: this.currentCandidatePair,
|
|
187
|
+
chipMap: this.chipMap,
|
|
188
|
+
})
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 4. If we've exited the loop, there are no more valid pairs to process
|
|
193
|
+
this.solved = true
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
override visualize() {
|
|
197
|
+
if (this.subSolver) {
|
|
198
|
+
return this.subSolver.visualize()
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const graphics = visualizeInputProblem(this.inputProblem)
|
|
202
|
+
|
|
203
|
+
// Draw solved long-distance traces
|
|
204
|
+
for (const trace of this.solvedLongDistanceTraces) {
|
|
205
|
+
graphics.lines!.push({
|
|
206
|
+
points: trace.tracePath,
|
|
207
|
+
strokeColor: "purple",
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Draw queued candidate pairs
|
|
212
|
+
for (const [p1, p2] of this.queuedCandidatePairs) {
|
|
213
|
+
graphics.lines!.push({
|
|
214
|
+
points: [p1, p2],
|
|
215
|
+
strokeColor: "gray",
|
|
216
|
+
strokeDash: "4 4",
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return graphics
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
public getOutput(): {
|
|
224
|
+
newTraces: SolvedTracePath[]
|
|
225
|
+
allTracesMerged: SolvedTracePath[]
|
|
226
|
+
} {
|
|
227
|
+
if (!this.solved) {
|
|
228
|
+
return { newTraces: [], allTracesMerged: this.params.alreadySolvedTraces }
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
newTraces: this.solvedLongDistanceTraces,
|
|
232
|
+
allTracesMerged: [
|
|
233
|
+
...this.params.alreadySolvedTraces,
|
|
234
|
+
...this.solvedLongDistanceTraces,
|
|
235
|
+
],
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -16,6 +16,7 @@ import { TraceLabelOverlapAvoidanceSolver } from "../TraceLabelOverlapAvoidanceS
|
|
|
16
16
|
import { getInputChipBounds } from "../GuidelinesSolver/getInputChipBounds"
|
|
17
17
|
import { correctPinsInsideChips } from "./correctPinsInsideChip"
|
|
18
18
|
import { expandChipsToFitPins } from "./expandChipsToFitPins"
|
|
19
|
+
import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePairSolver"
|
|
19
20
|
|
|
20
21
|
type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
|
|
21
22
|
solverName: string
|
|
@@ -24,6 +25,7 @@ type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
|
|
|
24
25
|
instance: SchematicTracePipelineSolver,
|
|
25
26
|
) => ConstructorParameters<T>
|
|
26
27
|
onSolved?: (instance: SchematicTracePipelineSolver) => void
|
|
28
|
+
shouldSkip?: (instance: SchematicTracePipelineSolver) => boolean
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
function definePipelineStep<
|
|
@@ -37,6 +39,7 @@ function definePipelineStep<
|
|
|
37
39
|
getConstructorParams: (instance: SchematicTracePipelineSolver) => P,
|
|
38
40
|
opts: {
|
|
39
41
|
onSolved?: (instance: SchematicTracePipelineSolver) => void
|
|
42
|
+
shouldSkip?: (instance: SchematicTracePipelineSolver) => boolean
|
|
40
43
|
} = {},
|
|
41
44
|
): PipelineStep<T> {
|
|
42
45
|
return {
|
|
@@ -44,13 +47,20 @@ function definePipelineStep<
|
|
|
44
47
|
solverClass,
|
|
45
48
|
getConstructorParams,
|
|
46
49
|
onSolved: opts.onSolved,
|
|
50
|
+
shouldSkip: opts.shouldSkip,
|
|
47
51
|
}
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
export interface SchematicTracePipelineSolverParams {
|
|
55
|
+
inputProblem: InputProblem
|
|
56
|
+
allowLongDistanceTraces?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
50
59
|
export class SchematicTracePipelineSolver extends BaseSolver {
|
|
51
60
|
mspConnectionPairSolver?: MspConnectionPairSolver
|
|
52
61
|
// guidelinesSolver?: GuidelinesSolver
|
|
53
62
|
schematicTraceLinesSolver?: SchematicTraceLinesSolver
|
|
63
|
+
longDistancePairSolver?: LongDistancePairSolver
|
|
54
64
|
traceOverlapShiftSolver?: TraceOverlapShiftSolver
|
|
55
65
|
netLabelPlacementSolver?: NetLabelPlacementSolver
|
|
56
66
|
traceLabelOverlapAvoidanceSolver?: TraceLabelOverlapAvoidanceSolver
|
|
@@ -96,6 +106,19 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
96
106
|
chipMap: this.mspConnectionPairSolver!.chipMap,
|
|
97
107
|
},
|
|
98
108
|
],
|
|
109
|
+
),
|
|
110
|
+
definePipelineStep(
|
|
111
|
+
"longDistancePairSolver",
|
|
112
|
+
LongDistancePairSolver,
|
|
113
|
+
(instance) => [
|
|
114
|
+
{
|
|
115
|
+
inputProblem: instance.inputProblem,
|
|
116
|
+
primaryMspConnectionPairs:
|
|
117
|
+
instance.mspConnectionPairSolver!.mspConnectionPairs,
|
|
118
|
+
alreadySolvedTraces:
|
|
119
|
+
instance.schematicTraceLinesSolver!.solvedTracePaths,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
99
122
|
{
|
|
100
123
|
onSolved: (schematicTraceLinesSolver) => {},
|
|
101
124
|
},
|
|
@@ -106,7 +129,8 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
106
129
|
() => [
|
|
107
130
|
{
|
|
108
131
|
inputProblem: this.inputProblem,
|
|
109
|
-
inputTracePaths:
|
|
132
|
+
inputTracePaths:
|
|
133
|
+
this.longDistancePairSolver?.getOutput().allTracesMerged!,
|
|
110
134
|
globalConnMap: this.mspConnectionPairSolver!.globalConnMap,
|
|
111
135
|
},
|
|
112
136
|
],
|
|
@@ -123,10 +147,9 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
123
147
|
inputTraceMap:
|
|
124
148
|
this.traceOverlapShiftSolver?.correctedTraceMap ??
|
|
125
149
|
Object.fromEntries(
|
|
126
|
-
this.
|
|
127
|
-
p.mspPairId,
|
|
128
|
-
|
|
129
|
-
]),
|
|
150
|
+
this.longDistancePairSolver!.getOutput().allTracesMerged.map(
|
|
151
|
+
(p) => [p.mspPairId, p],
|
|
152
|
+
),
|
|
130
153
|
),
|
|
131
154
|
},
|
|
132
155
|
],
|
|
@@ -143,10 +166,9 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
143
166
|
const traceMap =
|
|
144
167
|
instance.traceOverlapShiftSolver?.correctedTraceMap ??
|
|
145
168
|
Object.fromEntries(
|
|
146
|
-
instance
|
|
147
|
-
|
|
148
|
-
p,
|
|
149
|
-
]),
|
|
169
|
+
instance
|
|
170
|
+
.longDistancePairSolver!.getOutput()
|
|
171
|
+
.allTracesMerged.map((p) => [p.mspPairId, p]),
|
|
150
172
|
)
|
|
151
173
|
const traces = Object.values(traceMap)
|
|
152
174
|
const netLabelPlacements =
|
|
@@ -194,7 +194,9 @@ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver {
|
|
|
194
194
|
),
|
|
195
195
|
})
|
|
196
196
|
finalLabelPlacementSolver.solve()
|
|
197
|
-
|
|
197
|
+
if (!finalLabelPlacementSolver.failed) {
|
|
198
|
+
this.netLabelPlacements = finalLabelPlacementSolver.netLabelPlacements
|
|
199
|
+
}
|
|
198
200
|
|
|
199
201
|
this.solved = true
|
|
200
202
|
}
|