@tscircuit/schematic-trace-solver 0.0.7 → 0.0.9
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 -5
- package/dist/index.js +488 -344
- package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +29 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts +199 -465
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts +89 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/anchors.ts +11 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts +45 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts +48 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/host.ts +64 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts +177 -0
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver.ts +24 -3
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver.ts +38 -3
- package/package.json +1 -1
- package/site/SchematicTraceLinesSolver/SchematicTraceLinesSolver01.page.tsx +122 -0
- package/site/components/GenericSolverDebugger.tsx +16 -0
- package/site/examples/example01-basic.page.tsx +2 -2
- package/site/examples/example04-single-symbol.page.tsx +46 -0
- package/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro01.json +88 -0
- package/tests/solvers/SchematicTraceSingleLineSolver/SchematicTraceSingleLineSolver_repro01.test.ts +16 -0
package/dist/index.js
CHANGED
|
@@ -6694,12 +6694,33 @@ var SchematicTraceSingleLineSolver = class extends BaseSolver {
|
|
|
6694
6694
|
return;
|
|
6695
6695
|
}
|
|
6696
6696
|
const nextCandidatePath = this.queuedCandidatePaths.shift();
|
|
6697
|
-
const obstacleOps = {
|
|
6698
|
-
excludeChipIds: [this.pins[0].chipId, this.pins[1].chipId]
|
|
6699
|
-
};
|
|
6700
6697
|
for (let i = 0; i < nextCandidatePath.length - 1; i++) {
|
|
6701
6698
|
const start = nextCandidatePath[i];
|
|
6702
6699
|
const end = nextCandidatePath[i + 1];
|
|
6700
|
+
let excludeChipIds = [];
|
|
6701
|
+
const isStartPin = this.pins.some(
|
|
6702
|
+
(pin) => Math.abs(pin.x - start.x) < 1e-10 && Math.abs(pin.y - start.y) < 1e-10
|
|
6703
|
+
);
|
|
6704
|
+
const isEndPin = this.pins.some(
|
|
6705
|
+
(pin) => Math.abs(pin.x - end.x) < 1e-10 && Math.abs(pin.y - end.y) < 1e-10
|
|
6706
|
+
);
|
|
6707
|
+
if (isStartPin) {
|
|
6708
|
+
const startPin = this.pins.find(
|
|
6709
|
+
(pin) => Math.abs(pin.x - start.x) < 1e-10 && Math.abs(pin.y - start.y) < 1e-10
|
|
6710
|
+
);
|
|
6711
|
+
if (startPin && !excludeChipIds.includes(startPin.chipId)) {
|
|
6712
|
+
excludeChipIds.push(startPin.chipId);
|
|
6713
|
+
}
|
|
6714
|
+
}
|
|
6715
|
+
if (isEndPin) {
|
|
6716
|
+
const endPin = this.pins.find(
|
|
6717
|
+
(pin) => Math.abs(pin.x - end.x) < 1e-10 && Math.abs(pin.y - end.y) < 1e-10
|
|
6718
|
+
);
|
|
6719
|
+
if (endPin && !excludeChipIds.includes(endPin.chipId)) {
|
|
6720
|
+
excludeChipIds.push(endPin.chipId);
|
|
6721
|
+
}
|
|
6722
|
+
}
|
|
6723
|
+
const obstacleOps = { excludeChipIds };
|
|
6703
6724
|
const intersects = this.chipObstacleSpatialIndex.doesOrthogonalLineIntersectChip(
|
|
6704
6725
|
[start, end],
|
|
6705
6726
|
obstacleOps
|
|
@@ -6780,6 +6801,7 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
|
|
|
6780
6801
|
chipMap;
|
|
6781
6802
|
currentConnectionPair = null;
|
|
6782
6803
|
solvedTracePaths = [];
|
|
6804
|
+
failedConnectionPairs = [];
|
|
6783
6805
|
constructor(params) {
|
|
6784
6806
|
super();
|
|
6785
6807
|
this.inputProblem = params.inputProblem;
|
|
@@ -6810,9 +6832,14 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
|
|
|
6810
6832
|
this.currentConnectionPair = null;
|
|
6811
6833
|
}
|
|
6812
6834
|
if (this.activeSubSolver?.failed) {
|
|
6813
|
-
this.
|
|
6814
|
-
|
|
6815
|
-
|
|
6835
|
+
if (this.currentConnectionPair) {
|
|
6836
|
+
this.failedConnectionPairs.push({
|
|
6837
|
+
...this.currentConnectionPair,
|
|
6838
|
+
error: this.activeSubSolver.error || void 0
|
|
6839
|
+
});
|
|
6840
|
+
}
|
|
6841
|
+
this.activeSubSolver = null;
|
|
6842
|
+
this.currentConnectionPair = null;
|
|
6816
6843
|
}
|
|
6817
6844
|
if (this.activeSubSolver) {
|
|
6818
6845
|
this.activeSubSolver.step();
|
|
@@ -6848,6 +6875,17 @@ var SchematicTraceLinesSolver = class extends BaseSolver {
|
|
|
6848
6875
|
strokeColor: "green"
|
|
6849
6876
|
});
|
|
6850
6877
|
}
|
|
6878
|
+
for (const pair of this.failedConnectionPairs) {
|
|
6879
|
+
graphics.lines.push({
|
|
6880
|
+
points: [
|
|
6881
|
+
{ x: pair.pins[0].x, y: pair.pins[0].y },
|
|
6882
|
+
{ x: pair.pins[1].x, y: pair.pins[1].y }
|
|
6883
|
+
],
|
|
6884
|
+
strokeColor: "red",
|
|
6885
|
+
strokeDash: "4 2",
|
|
6886
|
+
strokeWidth: 4e-3
|
|
6887
|
+
});
|
|
6888
|
+
}
|
|
6851
6889
|
return graphics;
|
|
6852
6890
|
}
|
|
6853
6891
|
};
|
|
@@ -7150,9 +7188,292 @@ var TraceOverlapShiftSolver = class extends BaseSolver {
|
|
|
7150
7188
|
}
|
|
7151
7189
|
};
|
|
7152
7190
|
|
|
7153
|
-
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/
|
|
7191
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts
|
|
7154
7192
|
var NET_LABEL_HORIZONTAL_WIDTH = 0.4;
|
|
7155
7193
|
var NET_LABEL_HORIZONTAL_HEIGHT = 0.2;
|
|
7194
|
+
function getDimsForOrientation(orientation) {
|
|
7195
|
+
if (orientation === "y+" || orientation === "y-") {
|
|
7196
|
+
return {
|
|
7197
|
+
width: NET_LABEL_HORIZONTAL_HEIGHT,
|
|
7198
|
+
height: NET_LABEL_HORIZONTAL_WIDTH
|
|
7199
|
+
};
|
|
7200
|
+
}
|
|
7201
|
+
return {
|
|
7202
|
+
width: NET_LABEL_HORIZONTAL_WIDTH,
|
|
7203
|
+
height: NET_LABEL_HORIZONTAL_HEIGHT
|
|
7204
|
+
};
|
|
7205
|
+
}
|
|
7206
|
+
function getCenterFromAnchor(anchor, orientation, width, height) {
|
|
7207
|
+
switch (orientation) {
|
|
7208
|
+
case "x+":
|
|
7209
|
+
return { x: anchor.x + width / 2, y: anchor.y };
|
|
7210
|
+
case "x-":
|
|
7211
|
+
return { x: anchor.x - width / 2, y: anchor.y };
|
|
7212
|
+
case "y+":
|
|
7213
|
+
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
7214
|
+
case "y-":
|
|
7215
|
+
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
7216
|
+
}
|
|
7217
|
+
}
|
|
7218
|
+
function getRectBounds(center, w, h) {
|
|
7219
|
+
return {
|
|
7220
|
+
minX: center.x - w / 2,
|
|
7221
|
+
minY: center.y - h / 2,
|
|
7222
|
+
maxX: center.x + w / 2,
|
|
7223
|
+
maxY: center.y + h / 2
|
|
7224
|
+
};
|
|
7225
|
+
}
|
|
7226
|
+
|
|
7227
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions.ts
|
|
7228
|
+
function segmentIntersectsRect(p1, p2, rect, EPS = 1e-9) {
|
|
7229
|
+
const isVert = Math.abs(p1.x - p2.x) < EPS;
|
|
7230
|
+
const isHorz = Math.abs(p1.y - p2.y) < EPS;
|
|
7231
|
+
if (!isVert && !isHorz) return false;
|
|
7232
|
+
if (isVert) {
|
|
7233
|
+
const x = p1.x;
|
|
7234
|
+
if (x < rect.minX - EPS || x > rect.maxX + EPS) return false;
|
|
7235
|
+
const segMinY = Math.min(p1.y, p2.y);
|
|
7236
|
+
const segMaxY = Math.max(p1.y, p2.y);
|
|
7237
|
+
const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
|
|
7238
|
+
return overlap > EPS;
|
|
7239
|
+
} else {
|
|
7240
|
+
const y = p1.y;
|
|
7241
|
+
if (y < rect.minY - EPS || y > rect.maxY + EPS) return false;
|
|
7242
|
+
const segMinX = Math.min(p1.x, p2.x);
|
|
7243
|
+
const segMaxX = Math.max(p1.x, p2.x);
|
|
7244
|
+
const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
|
|
7245
|
+
return overlap > EPS;
|
|
7246
|
+
}
|
|
7247
|
+
}
|
|
7248
|
+
function rectIntersectsAnyTrace(bounds, inputTraceMap, hostPathId, hostSegIndex) {
|
|
7249
|
+
for (const [pairId, solved] of Object.entries(inputTraceMap)) {
|
|
7250
|
+
const pts = solved.tracePath;
|
|
7251
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
7252
|
+
if (pairId === hostPathId && i === hostSegIndex) continue;
|
|
7253
|
+
if (segmentIntersectsRect(pts[i], pts[i + 1], bounds)) return true;
|
|
7254
|
+
}
|
|
7255
|
+
}
|
|
7256
|
+
return false;
|
|
7257
|
+
}
|
|
7258
|
+
|
|
7259
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/host.ts
|
|
7260
|
+
function lengthOfTrace(path) {
|
|
7261
|
+
let sum = 0;
|
|
7262
|
+
const pts = path.tracePath;
|
|
7263
|
+
for (let i = 0; i < pts.length - 1; i++) {
|
|
7264
|
+
sum += Math.abs(pts[i + 1].x - pts[i].x) + Math.abs(pts[i + 1].y - pts[i].y);
|
|
7265
|
+
}
|
|
7266
|
+
return sum;
|
|
7267
|
+
}
|
|
7268
|
+
function chooseHostTraceForGroup(params) {
|
|
7269
|
+
const { inputProblem, inputTraceMap, globalConnNetId, fallbackTrace } = params;
|
|
7270
|
+
const chipsById = Object.fromEntries(
|
|
7271
|
+
inputProblem.chips.map((c) => [c.chipId, c])
|
|
7272
|
+
);
|
|
7273
|
+
const groupTraces = Object.values(inputTraceMap).filter(
|
|
7274
|
+
(t) => t.globalConnNetId === globalConnNetId
|
|
7275
|
+
);
|
|
7276
|
+
const chipIdsInGroup = /* @__PURE__ */ new Set();
|
|
7277
|
+
for (const t of groupTraces) {
|
|
7278
|
+
chipIdsInGroup.add(t.pins[0].chipId);
|
|
7279
|
+
chipIdsInGroup.add(t.pins[1].chipId);
|
|
7280
|
+
}
|
|
7281
|
+
let largestChipId = null;
|
|
7282
|
+
let largestPinCount = -1;
|
|
7283
|
+
for (const id of chipIdsInGroup) {
|
|
7284
|
+
const chip = chipsById[id];
|
|
7285
|
+
const count = chip?.pins?.length ?? 0;
|
|
7286
|
+
if (count > largestPinCount) {
|
|
7287
|
+
largestPinCount = count;
|
|
7288
|
+
largestChipId = id;
|
|
7289
|
+
}
|
|
7290
|
+
}
|
|
7291
|
+
const hostCandidates = largestChipId == null ? [] : groupTraces.filter(
|
|
7292
|
+
(t) => t.pins[0].chipId === largestChipId || t.pins[1].chipId === largestChipId
|
|
7293
|
+
);
|
|
7294
|
+
if (hostCandidates.length > 0) {
|
|
7295
|
+
return hostCandidates.reduce(
|
|
7296
|
+
(a, b) => lengthOfTrace(a) >= lengthOfTrace(b) ? a : b
|
|
7297
|
+
);
|
|
7298
|
+
}
|
|
7299
|
+
return fallbackTrace;
|
|
7300
|
+
}
|
|
7301
|
+
|
|
7302
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/anchors.ts
|
|
7303
|
+
function anchorsForSegment(a, b) {
|
|
7304
|
+
return [
|
|
7305
|
+
{ x: a.x, y: a.y },
|
|
7306
|
+
{ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },
|
|
7307
|
+
{ x: b.x, y: b.y }
|
|
7308
|
+
];
|
|
7309
|
+
}
|
|
7310
|
+
|
|
7311
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts
|
|
7312
|
+
function solveNetLabelPlacementForPortOnlyPin(params) {
|
|
7313
|
+
const {
|
|
7314
|
+
inputProblem,
|
|
7315
|
+
inputTraceMap,
|
|
7316
|
+
chipObstacleSpatialIndex,
|
|
7317
|
+
overlappingSameNetTraceGroup,
|
|
7318
|
+
availableOrientations
|
|
7319
|
+
} = params;
|
|
7320
|
+
const pinId = overlappingSameNetTraceGroup.portOnlyPinId;
|
|
7321
|
+
if (!pinId) {
|
|
7322
|
+
return {
|
|
7323
|
+
placement: null,
|
|
7324
|
+
testedCandidates: [],
|
|
7325
|
+
error: "No portOnlyPinId provided"
|
|
7326
|
+
};
|
|
7327
|
+
}
|
|
7328
|
+
let pin = null;
|
|
7329
|
+
for (const chip of inputProblem.chips) {
|
|
7330
|
+
const p = chip.pins.find((pp) => pp.pinId === pinId);
|
|
7331
|
+
if (p) {
|
|
7332
|
+
pin = { x: p.x, y: p.y };
|
|
7333
|
+
break;
|
|
7334
|
+
}
|
|
7335
|
+
}
|
|
7336
|
+
if (!pin) {
|
|
7337
|
+
return {
|
|
7338
|
+
placement: null,
|
|
7339
|
+
testedCandidates: [],
|
|
7340
|
+
error: `Port-only pin not found: ${pinId}`
|
|
7341
|
+
};
|
|
7342
|
+
}
|
|
7343
|
+
const orientations = availableOrientations.length > 0 ? availableOrientations : ["x+", "x-", "y+", "y-"];
|
|
7344
|
+
const anchor = { x: pin.x, y: pin.y };
|
|
7345
|
+
const outwardOf = (o) => o === "x+" ? { x: 1, y: 0 } : o === "x-" ? { x: -1, y: 0 } : o === "y+" ? { x: 0, y: 1 } : { x: 0, y: -1 };
|
|
7346
|
+
const testedCandidates = [];
|
|
7347
|
+
for (const orientation of orientations) {
|
|
7348
|
+
const { width, height } = getDimsForOrientation(orientation);
|
|
7349
|
+
const baseCenter = getCenterFromAnchor(anchor, orientation, width, height);
|
|
7350
|
+
const outward = outwardOf(orientation);
|
|
7351
|
+
const offset = 1e-3;
|
|
7352
|
+
const center = {
|
|
7353
|
+
x: baseCenter.x + outward.x * offset,
|
|
7354
|
+
y: baseCenter.y + outward.y * offset
|
|
7355
|
+
};
|
|
7356
|
+
const bounds = getRectBounds(center, width, height);
|
|
7357
|
+
const chips = chipObstacleSpatialIndex.getChipsInBounds(bounds);
|
|
7358
|
+
if (chips.length > 0) {
|
|
7359
|
+
testedCandidates.push({
|
|
7360
|
+
center,
|
|
7361
|
+
width,
|
|
7362
|
+
height,
|
|
7363
|
+
bounds,
|
|
7364
|
+
anchor,
|
|
7365
|
+
orientation,
|
|
7366
|
+
status: "chip-collision",
|
|
7367
|
+
hostSegIndex: -1
|
|
7368
|
+
});
|
|
7369
|
+
continue;
|
|
7370
|
+
}
|
|
7371
|
+
if (rectIntersectsAnyTrace(
|
|
7372
|
+
bounds,
|
|
7373
|
+
inputTraceMap,
|
|
7374
|
+
"",
|
|
7375
|
+
-1
|
|
7376
|
+
)) {
|
|
7377
|
+
testedCandidates.push({
|
|
7378
|
+
center,
|
|
7379
|
+
width,
|
|
7380
|
+
height,
|
|
7381
|
+
bounds,
|
|
7382
|
+
anchor,
|
|
7383
|
+
orientation,
|
|
7384
|
+
status: "trace-collision",
|
|
7385
|
+
hostSegIndex: -1
|
|
7386
|
+
});
|
|
7387
|
+
continue;
|
|
7388
|
+
}
|
|
7389
|
+
testedCandidates.push({
|
|
7390
|
+
center,
|
|
7391
|
+
width,
|
|
7392
|
+
height,
|
|
7393
|
+
bounds,
|
|
7394
|
+
anchor,
|
|
7395
|
+
orientation,
|
|
7396
|
+
status: "ok",
|
|
7397
|
+
hostSegIndex: -1
|
|
7398
|
+
});
|
|
7399
|
+
const placement = {
|
|
7400
|
+
globalConnNetId: overlappingSameNetTraceGroup.globalConnNetId,
|
|
7401
|
+
dcConnNetId: void 0,
|
|
7402
|
+
orientation,
|
|
7403
|
+
anchorPoint: anchor,
|
|
7404
|
+
width,
|
|
7405
|
+
height,
|
|
7406
|
+
center
|
|
7407
|
+
};
|
|
7408
|
+
return { placement, testedCandidates };
|
|
7409
|
+
}
|
|
7410
|
+
return {
|
|
7411
|
+
placement: null,
|
|
7412
|
+
testedCandidates,
|
|
7413
|
+
error: "Could not place net label at port without collisions"
|
|
7414
|
+
};
|
|
7415
|
+
}
|
|
7416
|
+
|
|
7417
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver_visualize.ts
|
|
7418
|
+
function visualizeSingleNetLabelPlacementSolver(solver) {
|
|
7419
|
+
const graphics = visualizeInputProblem(solver.inputProblem);
|
|
7420
|
+
const groupId = solver.overlappingSameNetTraceGroup.globalConnNetId;
|
|
7421
|
+
const host = chooseHostTraceForGroup({
|
|
7422
|
+
inputProblem: solver.inputProblem,
|
|
7423
|
+
inputTraceMap: solver.inputTraceMap,
|
|
7424
|
+
globalConnNetId: groupId,
|
|
7425
|
+
fallbackTrace: solver.overlappingSameNetTraceGroup.overlappingTraces
|
|
7426
|
+
});
|
|
7427
|
+
const groupStroke = getColorFromString(groupId, 0.9);
|
|
7428
|
+
const groupFill = getColorFromString(groupId, 0.5);
|
|
7429
|
+
for (const trace of Object.values(solver.inputTraceMap)) {
|
|
7430
|
+
if (trace.globalConnNetId !== groupId) continue;
|
|
7431
|
+
const isHost = host ? trace.mspPairId === host.mspPairId : false;
|
|
7432
|
+
graphics.lines.push({
|
|
7433
|
+
points: trace.tracePath,
|
|
7434
|
+
strokeColor: isHost ? groupStroke : groupFill,
|
|
7435
|
+
strokeWidth: isHost ? 6e-3 : 3e-3,
|
|
7436
|
+
strokeDash: isHost ? void 0 : "4 2"
|
|
7437
|
+
});
|
|
7438
|
+
}
|
|
7439
|
+
for (const c of solver.testedCandidates) {
|
|
7440
|
+
const fill = c.status === "ok" ? "rgba(0, 180, 0, 0.25)" : c.status === "chip-collision" ? "rgba(220, 0, 0, 0.25)" : c.status === "trace-collision" ? "rgba(220, 140, 0, 0.25)" : "rgba(120, 120, 120, 0.15)";
|
|
7441
|
+
const stroke = c.status === "ok" ? "green" : c.status === "chip-collision" ? "red" : c.status === "trace-collision" ? "orange" : "gray";
|
|
7442
|
+
graphics.rects.push({
|
|
7443
|
+
center: {
|
|
7444
|
+
x: (c.bounds.minX + c.bounds.maxX) / 2,
|
|
7445
|
+
y: (c.bounds.minY + c.bounds.maxY) / 2
|
|
7446
|
+
},
|
|
7447
|
+
width: c.width,
|
|
7448
|
+
height: c.height,
|
|
7449
|
+
fill,
|
|
7450
|
+
strokeColor: stroke
|
|
7451
|
+
});
|
|
7452
|
+
graphics.points.push({
|
|
7453
|
+
x: c.anchor.x,
|
|
7454
|
+
y: c.anchor.y,
|
|
7455
|
+
color: stroke
|
|
7456
|
+
});
|
|
7457
|
+
}
|
|
7458
|
+
if (solver.netLabelPlacement) {
|
|
7459
|
+
const p = solver.netLabelPlacement;
|
|
7460
|
+
graphics.rects.push({
|
|
7461
|
+
center: p.center,
|
|
7462
|
+
width: p.width,
|
|
7463
|
+
height: p.height,
|
|
7464
|
+
fill: "rgba(0, 128, 255, 0.35)",
|
|
7465
|
+
strokeColor: "blue"
|
|
7466
|
+
});
|
|
7467
|
+
graphics.points.push({
|
|
7468
|
+
x: p.anchorPoint.x,
|
|
7469
|
+
y: p.anchorPoint.y,
|
|
7470
|
+
color: "blue"
|
|
7471
|
+
});
|
|
7472
|
+
}
|
|
7473
|
+
return graphics;
|
|
7474
|
+
}
|
|
7475
|
+
|
|
7476
|
+
// lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts
|
|
7156
7477
|
var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
7157
7478
|
inputProblem;
|
|
7158
7479
|
inputTraceMap;
|
|
@@ -7169,260 +7490,127 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
7169
7490
|
this.availableOrientations = params.availableOrientations;
|
|
7170
7491
|
this.chipObstacleSpatialIndex = params.inputProblem._chipObstacleSpatialIndex ?? new ChipObstacleSpatialIndex(params.inputProblem.chips);
|
|
7171
7492
|
}
|
|
7172
|
-
getDimsForOrientation(orientation) {
|
|
7173
|
-
if (orientation === "y+" || orientation === "y-") {
|
|
7174
|
-
return {
|
|
7175
|
-
width: NET_LABEL_HORIZONTAL_HEIGHT,
|
|
7176
|
-
height: NET_LABEL_HORIZONTAL_WIDTH
|
|
7177
|
-
};
|
|
7178
|
-
}
|
|
7179
|
-
return {
|
|
7180
|
-
width: NET_LABEL_HORIZONTAL_WIDTH,
|
|
7181
|
-
height: NET_LABEL_HORIZONTAL_HEIGHT
|
|
7182
|
-
};
|
|
7183
|
-
}
|
|
7184
|
-
getCenterFromAnchor(anchor, orientation, width, height) {
|
|
7185
|
-
switch (orientation) {
|
|
7186
|
-
case "x+":
|
|
7187
|
-
return { x: anchor.x + width / 2, y: anchor.y };
|
|
7188
|
-
case "x-":
|
|
7189
|
-
return { x: anchor.x - width / 2, y: anchor.y };
|
|
7190
|
-
case "y+":
|
|
7191
|
-
return { x: anchor.x, y: anchor.y + height / 2 };
|
|
7192
|
-
case "y-":
|
|
7193
|
-
return { x: anchor.x, y: anchor.y - height / 2 };
|
|
7194
|
-
}
|
|
7195
|
-
}
|
|
7196
|
-
getRectBounds(center, w, h) {
|
|
7197
|
-
return {
|
|
7198
|
-
minX: center.x - w / 2,
|
|
7199
|
-
minY: center.y - h / 2,
|
|
7200
|
-
maxX: center.x + w / 2,
|
|
7201
|
-
maxY: center.y + h / 2
|
|
7202
|
-
};
|
|
7203
|
-
}
|
|
7204
|
-
segmentIntersectsRect(p1, p2, rect, EPS = 1e-9) {
|
|
7205
|
-
const isVert = Math.abs(p1.x - p2.x) < EPS;
|
|
7206
|
-
const isHorz = Math.abs(p1.y - p2.y) < EPS;
|
|
7207
|
-
if (!isVert && !isHorz) return false;
|
|
7208
|
-
if (isVert) {
|
|
7209
|
-
const x = p1.x;
|
|
7210
|
-
if (x < rect.minX - EPS || x > rect.maxX + EPS) return false;
|
|
7211
|
-
const segMinY = Math.min(p1.y, p2.y);
|
|
7212
|
-
const segMaxY = Math.max(p1.y, p2.y);
|
|
7213
|
-
const overlap = Math.min(segMaxY, rect.maxY) - Math.max(segMinY, rect.minY);
|
|
7214
|
-
return overlap > EPS;
|
|
7215
|
-
} else {
|
|
7216
|
-
const y = p1.y;
|
|
7217
|
-
if (y < rect.minY - EPS || y > rect.maxY + EPS) return false;
|
|
7218
|
-
const segMinX = Math.min(p1.x, p2.x);
|
|
7219
|
-
const segMaxX = Math.max(p1.x, p2.x);
|
|
7220
|
-
const overlap = Math.min(segMaxX, rect.maxX) - Math.max(segMinX, rect.minX);
|
|
7221
|
-
return overlap > EPS;
|
|
7222
|
-
}
|
|
7223
|
-
}
|
|
7224
|
-
rectIntersectsAnyTrace(bounds, hostPathId, hostSegIndex) {
|
|
7225
|
-
for (const [pairId, solved] of Object.entries(this.inputTraceMap)) {
|
|
7226
|
-
const pts = solved.tracePath;
|
|
7227
|
-
for (let i = 0; i < pts.length - 1; i++) {
|
|
7228
|
-
if (pairId === hostPathId && i === hostSegIndex) continue;
|
|
7229
|
-
if (this.segmentIntersectsRect(pts[i], pts[i + 1], bounds))
|
|
7230
|
-
return true;
|
|
7231
|
-
}
|
|
7232
|
-
}
|
|
7233
|
-
return false;
|
|
7234
|
-
}
|
|
7235
7493
|
_step() {
|
|
7236
7494
|
if (this.netLabelPlacement) {
|
|
7237
7495
|
this.solved = true;
|
|
7238
7496
|
return;
|
|
7239
7497
|
}
|
|
7240
7498
|
if (this.overlappingSameNetTraceGroup.portOnlyPinId) {
|
|
7241
|
-
const
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7246
|
-
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7251
|
-
this.failed = true;
|
|
7252
|
-
this.error = `Port-only pin not found: ${pinId}`;
|
|
7253
|
-
return;
|
|
7254
|
-
}
|
|
7255
|
-
const orientations2 = this.availableOrientations.length > 0 ? this.availableOrientations : ["x+", "x-", "y+", "y-"];
|
|
7256
|
-
const anchor = { x: pin.x, y: pin.y };
|
|
7257
|
-
const outwardOf = (o) => o === "x+" ? { x: 1, y: 0 } : o === "x-" ? { x: -1, y: 0 } : o === "y+" ? { x: 0, y: 1 } : { x: 0, y: -1 };
|
|
7258
|
-
for (const orientation of orientations2) {
|
|
7259
|
-
const { width, height } = this.getDimsForOrientation(orientation);
|
|
7260
|
-
const baseCenter = this.getCenterFromAnchor(
|
|
7261
|
-
anchor,
|
|
7262
|
-
orientation,
|
|
7263
|
-
width,
|
|
7264
|
-
height
|
|
7265
|
-
);
|
|
7266
|
-
const outward = outwardOf(orientation);
|
|
7267
|
-
const offset = 1e-3;
|
|
7268
|
-
const center = {
|
|
7269
|
-
x: baseCenter.x + outward.x * offset,
|
|
7270
|
-
y: baseCenter.y + outward.y * offset
|
|
7271
|
-
};
|
|
7272
|
-
const bounds = this.getRectBounds(center, width, height);
|
|
7273
|
-
const chips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds);
|
|
7274
|
-
if (chips.length > 0) {
|
|
7275
|
-
this.testedCandidates.push({
|
|
7276
|
-
center,
|
|
7277
|
-
width,
|
|
7278
|
-
height,
|
|
7279
|
-
bounds,
|
|
7280
|
-
anchor,
|
|
7281
|
-
orientation,
|
|
7282
|
-
status: "chip-collision",
|
|
7283
|
-
hostSegIndex: -1
|
|
7284
|
-
});
|
|
7285
|
-
continue;
|
|
7286
|
-
}
|
|
7287
|
-
if (this.rectIntersectsAnyTrace(bounds, "", -1)) {
|
|
7288
|
-
this.testedCandidates.push({
|
|
7289
|
-
center,
|
|
7290
|
-
width,
|
|
7291
|
-
height,
|
|
7292
|
-
bounds,
|
|
7293
|
-
anchor,
|
|
7294
|
-
orientation,
|
|
7295
|
-
status: "trace-collision",
|
|
7296
|
-
hostSegIndex: -1
|
|
7297
|
-
});
|
|
7298
|
-
continue;
|
|
7299
|
-
}
|
|
7300
|
-
this.testedCandidates.push({
|
|
7301
|
-
center,
|
|
7302
|
-
width,
|
|
7303
|
-
height,
|
|
7304
|
-
bounds,
|
|
7305
|
-
anchor,
|
|
7306
|
-
orientation,
|
|
7307
|
-
status: "ok",
|
|
7308
|
-
hostSegIndex: -1
|
|
7309
|
-
});
|
|
7310
|
-
this.netLabelPlacement = {
|
|
7311
|
-
globalConnNetId: this.overlappingSameNetTraceGroup.globalConnNetId,
|
|
7312
|
-
dcConnNetId: void 0,
|
|
7313
|
-
orientation,
|
|
7314
|
-
anchorPoint: anchor,
|
|
7315
|
-
width,
|
|
7316
|
-
height,
|
|
7317
|
-
center
|
|
7318
|
-
};
|
|
7499
|
+
const res = solveNetLabelPlacementForPortOnlyPin({
|
|
7500
|
+
inputProblem: this.inputProblem,
|
|
7501
|
+
inputTraceMap: this.inputTraceMap,
|
|
7502
|
+
chipObstacleSpatialIndex: this.chipObstacleSpatialIndex,
|
|
7503
|
+
overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
|
|
7504
|
+
availableOrientations: this.availableOrientations
|
|
7505
|
+
});
|
|
7506
|
+
this.testedCandidates.push(...res.testedCandidates);
|
|
7507
|
+
if (res.placement) {
|
|
7508
|
+
this.netLabelPlacement = res.placement;
|
|
7319
7509
|
this.solved = true;
|
|
7320
7510
|
return;
|
|
7321
7511
|
}
|
|
7322
7512
|
this.failed = true;
|
|
7323
|
-
this.error = "Could not place net label at port without collisions";
|
|
7513
|
+
this.error = res.error ?? "Could not place net label at port without collisions";
|
|
7324
7514
|
return;
|
|
7325
7515
|
}
|
|
7326
7516
|
const groupId = this.overlappingSameNetTraceGroup.globalConnNetId;
|
|
7327
|
-
|
|
7328
|
-
this.inputProblem
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
);
|
|
7333
|
-
const chipIdsInGroup = /* @__PURE__ */ new Set();
|
|
7334
|
-
for (const t of groupTraces) {
|
|
7335
|
-
chipIdsInGroup.add(t.pins[0].chipId);
|
|
7336
|
-
chipIdsInGroup.add(t.pins[1].chipId);
|
|
7337
|
-
}
|
|
7338
|
-
let largestChipId = null;
|
|
7339
|
-
let largestPinCount = -1;
|
|
7340
|
-
for (const id of chipIdsInGroup) {
|
|
7341
|
-
const chip = chipsById[id];
|
|
7342
|
-
const count = chip?.pins?.length ?? 0;
|
|
7343
|
-
if (count > largestPinCount) {
|
|
7344
|
-
largestPinCount = count;
|
|
7345
|
-
largestChipId = id;
|
|
7346
|
-
}
|
|
7347
|
-
}
|
|
7348
|
-
const lengthOf = (path) => {
|
|
7349
|
-
let sum = 0;
|
|
7350
|
-
const pts2 = path.tracePath;
|
|
7351
|
-
for (let i = 0; i < pts2.length - 1; i++) {
|
|
7352
|
-
sum += Math.abs(pts2[i + 1].x - pts2[i].x) + Math.abs(pts2[i + 1].y - pts2[i].y);
|
|
7353
|
-
}
|
|
7354
|
-
return sum;
|
|
7355
|
-
};
|
|
7356
|
-
const hostCandidates = largestChipId == null ? [] : groupTraces.filter(
|
|
7357
|
-
(t) => t.pins[0].chipId === largestChipId || t.pins[1].chipId === largestChipId
|
|
7358
|
-
);
|
|
7359
|
-
let host = hostCandidates.length > 0 ? hostCandidates.reduce((a, b) => lengthOf(a) >= lengthOf(b) ? a : b) : this.overlappingSameNetTraceGroup.overlappingTraces;
|
|
7517
|
+
let host = chooseHostTraceForGroup({
|
|
7518
|
+
inputProblem: this.inputProblem,
|
|
7519
|
+
inputTraceMap: this.inputTraceMap,
|
|
7520
|
+
globalConnNetId: groupId,
|
|
7521
|
+
fallbackTrace: this.overlappingSameNetTraceGroup.overlappingTraces
|
|
7522
|
+
});
|
|
7360
7523
|
if (!host) {
|
|
7361
7524
|
this.failed = true;
|
|
7362
7525
|
this.error = "No host trace found for net label placement";
|
|
7363
7526
|
return;
|
|
7364
7527
|
}
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
}
|
|
7373
|
-
}
|
|
7528
|
+
const tracesToScanBase = Object.values(this.inputTraceMap).filter(
|
|
7529
|
+
(t) => t.globalConnNetId === groupId
|
|
7530
|
+
);
|
|
7531
|
+
const tracesToScan = this.availableOrientations.length === 1 ? [
|
|
7532
|
+
host,
|
|
7533
|
+
...tracesToScanBase.filter((t) => t.mspPairId !== host.mspPairId)
|
|
7534
|
+
] : [host];
|
|
7374
7535
|
const orientations = this.availableOrientations.length > 0 ? this.availableOrientations : ["x+", "x-", "y+", "y-"];
|
|
7536
|
+
const singleOrientationMode = this.availableOrientations.length === 1;
|
|
7537
|
+
const scoreFor = (orientation, anchor) => {
|
|
7538
|
+
switch (orientation) {
|
|
7539
|
+
case "y+":
|
|
7540
|
+
return anchor.y;
|
|
7541
|
+
case "y-":
|
|
7542
|
+
return -anchor.y;
|
|
7543
|
+
case "x+":
|
|
7544
|
+
return anchor.x;
|
|
7545
|
+
case "x-":
|
|
7546
|
+
return -anchor.x;
|
|
7547
|
+
}
|
|
7548
|
+
};
|
|
7549
|
+
let bestCandidate = null;
|
|
7550
|
+
let bestScore = -Infinity;
|
|
7375
7551
|
const EPS = 1e-6;
|
|
7376
|
-
const
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
|
|
7381
|
-
|
|
7382
|
-
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
const anchors = anchorsForSegment(a, b);
|
|
7395
|
-
for (const anchor of anchors) {
|
|
7396
|
-
for (const orientation of candidateOrients) {
|
|
7397
|
-
const { width, height } = this.getDimsForOrientation(orientation);
|
|
7398
|
-
const center = this.getCenterFromAnchor(
|
|
7399
|
-
anchor,
|
|
7400
|
-
orientation,
|
|
7401
|
-
width,
|
|
7402
|
-
height
|
|
7403
|
-
);
|
|
7404
|
-
const outward = orientation === "x+" ? { x: 1, y: 0 } : orientation === "x-" ? { x: -1, y: 0 } : orientation === "y+" ? { x: 0, y: 1 } : { x: 0, y: -1 };
|
|
7405
|
-
const offset = 1e-4;
|
|
7406
|
-
const testCenter = {
|
|
7407
|
-
x: center.x + outward.x * offset,
|
|
7408
|
-
y: center.y + outward.y * offset
|
|
7409
|
-
};
|
|
7410
|
-
const bounds = this.getRectBounds(testCenter, width, height);
|
|
7411
|
-
const chips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds);
|
|
7412
|
-
if (chips.length > 0) {
|
|
7413
|
-
this.testedCandidates.push({
|
|
7414
|
-
center: testCenter,
|
|
7415
|
-
width,
|
|
7416
|
-
height,
|
|
7417
|
-
bounds,
|
|
7552
|
+
for (const curr of tracesToScan) {
|
|
7553
|
+
const pts = curr.tracePath.slice();
|
|
7554
|
+
for (let si = 0; si < pts.length - 1; si++) {
|
|
7555
|
+
const a = pts[si];
|
|
7556
|
+
const b = pts[si + 1];
|
|
7557
|
+
const isH = Math.abs(a.y - b.y) < EPS;
|
|
7558
|
+
const isV = Math.abs(a.x - b.x) < EPS;
|
|
7559
|
+
if (!isH && !isV) continue;
|
|
7560
|
+
const segmentAllowed = isH ? ["y+", "y-"] : ["x+", "x-"];
|
|
7561
|
+
const candidateOrients = orientations.filter(
|
|
7562
|
+
(o) => segmentAllowed.includes(o)
|
|
7563
|
+
);
|
|
7564
|
+
if (candidateOrients.length === 0) continue;
|
|
7565
|
+
const anchors = anchorsForSegment(a, b);
|
|
7566
|
+
for (const anchor of anchors) {
|
|
7567
|
+
for (const orientation of candidateOrients) {
|
|
7568
|
+
const { width, height } = getDimsForOrientation(orientation);
|
|
7569
|
+
const center = getCenterFromAnchor(
|
|
7418
7570
|
anchor,
|
|
7419
7571
|
orientation,
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
|
|
7424
|
-
|
|
7425
|
-
|
|
7572
|
+
width,
|
|
7573
|
+
height
|
|
7574
|
+
);
|
|
7575
|
+
const outward = orientation === "x+" ? { x: 1, y: 0 } : orientation === "x-" ? { x: -1, y: 0 } : orientation === "y+" ? { x: 0, y: 1 } : { x: 0, y: -1 };
|
|
7576
|
+
const offset = 1e-4;
|
|
7577
|
+
const testCenter = {
|
|
7578
|
+
x: center.x + outward.x * offset,
|
|
7579
|
+
y: center.y + outward.y * offset
|
|
7580
|
+
};
|
|
7581
|
+
const bounds = getRectBounds(testCenter, width, height);
|
|
7582
|
+
const chips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds);
|
|
7583
|
+
if (chips.length > 0) {
|
|
7584
|
+
this.testedCandidates.push({
|
|
7585
|
+
center: testCenter,
|
|
7586
|
+
width,
|
|
7587
|
+
height,
|
|
7588
|
+
bounds,
|
|
7589
|
+
anchor,
|
|
7590
|
+
orientation,
|
|
7591
|
+
status: "chip-collision",
|
|
7592
|
+
hostSegIndex: si
|
|
7593
|
+
});
|
|
7594
|
+
continue;
|
|
7595
|
+
}
|
|
7596
|
+
if (rectIntersectsAnyTrace(
|
|
7597
|
+
bounds,
|
|
7598
|
+
this.inputTraceMap,
|
|
7599
|
+
curr.mspPairId,
|
|
7600
|
+
si
|
|
7601
|
+
)) {
|
|
7602
|
+
this.testedCandidates.push({
|
|
7603
|
+
center: testCenter,
|
|
7604
|
+
width,
|
|
7605
|
+
height,
|
|
7606
|
+
bounds,
|
|
7607
|
+
anchor,
|
|
7608
|
+
orientation,
|
|
7609
|
+
status: "trace-collision",
|
|
7610
|
+
hostSegIndex: si
|
|
7611
|
+
});
|
|
7612
|
+
continue;
|
|
7613
|
+
}
|
|
7426
7614
|
this.testedCandidates.push({
|
|
7427
7615
|
center: testCenter,
|
|
7428
7616
|
width,
|
|
@@ -7430,121 +7618,58 @@ var SingleNetLabelPlacementSolver = class extends BaseSolver {
|
|
|
7430
7618
|
bounds,
|
|
7431
7619
|
anchor,
|
|
7432
7620
|
orientation,
|
|
7433
|
-
status: "
|
|
7621
|
+
status: "ok",
|
|
7434
7622
|
hostSegIndex: si
|
|
7435
7623
|
});
|
|
7436
|
-
|
|
7624
|
+
if (singleOrientationMode) {
|
|
7625
|
+
const s = scoreFor(orientation, anchor);
|
|
7626
|
+
if (s > bestScore + 1e-9) {
|
|
7627
|
+
bestScore = s;
|
|
7628
|
+
bestCandidate = {
|
|
7629
|
+
anchor,
|
|
7630
|
+
orientation,
|
|
7631
|
+
width,
|
|
7632
|
+
height,
|
|
7633
|
+
center,
|
|
7634
|
+
hostSegIndex: si,
|
|
7635
|
+
dcConnNetId: curr.dcConnNetId
|
|
7636
|
+
};
|
|
7637
|
+
}
|
|
7638
|
+
continue;
|
|
7639
|
+
}
|
|
7640
|
+
this.netLabelPlacement = {
|
|
7641
|
+
globalConnNetId: this.overlappingSameNetTraceGroup.globalConnNetId,
|
|
7642
|
+
dcConnNetId: curr.dcConnNetId,
|
|
7643
|
+
orientation,
|
|
7644
|
+
anchorPoint: anchor,
|
|
7645
|
+
width,
|
|
7646
|
+
height,
|
|
7647
|
+
center
|
|
7648
|
+
};
|
|
7649
|
+
this.solved = true;
|
|
7650
|
+
return;
|
|
7437
7651
|
}
|
|
7438
|
-
this.testedCandidates.push({
|
|
7439
|
-
center: testCenter,
|
|
7440
|
-
width,
|
|
7441
|
-
height,
|
|
7442
|
-
bounds,
|
|
7443
|
-
anchor,
|
|
7444
|
-
orientation,
|
|
7445
|
-
status: "ok",
|
|
7446
|
-
hostSegIndex: si
|
|
7447
|
-
});
|
|
7448
|
-
this.netLabelPlacement = {
|
|
7449
|
-
globalConnNetId: this.overlappingSameNetTraceGroup.globalConnNetId,
|
|
7450
|
-
dcConnNetId: host.dcConnNetId,
|
|
7451
|
-
orientation,
|
|
7452
|
-
anchorPoint: anchor,
|
|
7453
|
-
width,
|
|
7454
|
-
height,
|
|
7455
|
-
center
|
|
7456
|
-
};
|
|
7457
|
-
this.solved = true;
|
|
7458
|
-
return;
|
|
7459
7652
|
}
|
|
7460
7653
|
}
|
|
7461
7654
|
}
|
|
7655
|
+
if (singleOrientationMode && bestCandidate) {
|
|
7656
|
+
this.netLabelPlacement = {
|
|
7657
|
+
globalConnNetId: this.overlappingSameNetTraceGroup.globalConnNetId,
|
|
7658
|
+
dcConnNetId: bestCandidate.dcConnNetId,
|
|
7659
|
+
orientation: bestCandidate.orientation,
|
|
7660
|
+
anchorPoint: bestCandidate.anchor,
|
|
7661
|
+
width: bestCandidate.width,
|
|
7662
|
+
height: bestCandidate.height,
|
|
7663
|
+
center: bestCandidate.center
|
|
7664
|
+
};
|
|
7665
|
+
this.solved = true;
|
|
7666
|
+
return;
|
|
7667
|
+
}
|
|
7462
7668
|
this.failed = true;
|
|
7463
7669
|
this.error = "Could not place net label without collisions";
|
|
7464
7670
|
}
|
|
7465
7671
|
visualize() {
|
|
7466
|
-
|
|
7467
|
-
const groupId = this.overlappingSameNetTraceGroup.globalConnNetId;
|
|
7468
|
-
const chipsById = Object.fromEntries(
|
|
7469
|
-
this.inputProblem.chips.map((c) => [c.chipId, c])
|
|
7470
|
-
);
|
|
7471
|
-
const groupTraces = Object.values(this.inputTraceMap).filter(
|
|
7472
|
-
(t) => t.globalConnNetId === groupId
|
|
7473
|
-
);
|
|
7474
|
-
const chipIdsInGroup = /* @__PURE__ */ new Set();
|
|
7475
|
-
for (const t of groupTraces) {
|
|
7476
|
-
chipIdsInGroup.add(t.pins[0].chipId);
|
|
7477
|
-
chipIdsInGroup.add(t.pins[1].chipId);
|
|
7478
|
-
}
|
|
7479
|
-
let largestChipId = null;
|
|
7480
|
-
let largestPinCount = -1;
|
|
7481
|
-
for (const id of chipIdsInGroup) {
|
|
7482
|
-
const chip = chipsById[id];
|
|
7483
|
-
const count = chip?.pins?.length ?? 0;
|
|
7484
|
-
if (count > largestPinCount) {
|
|
7485
|
-
largestPinCount = count;
|
|
7486
|
-
largestChipId = id;
|
|
7487
|
-
}
|
|
7488
|
-
}
|
|
7489
|
-
const lengthOf = (path) => {
|
|
7490
|
-
let sum = 0;
|
|
7491
|
-
const pts = path.tracePath;
|
|
7492
|
-
for (let i = 0; i < pts.length - 1; i++) {
|
|
7493
|
-
sum += Math.abs(pts[i + 1].x - pts[i].x) + Math.abs(pts[i + 1].y - pts[i].y);
|
|
7494
|
-
}
|
|
7495
|
-
return sum;
|
|
7496
|
-
};
|
|
7497
|
-
const hostCandidates = largestChipId == null ? [] : groupTraces.filter(
|
|
7498
|
-
(t) => t.pins[0].chipId === largestChipId || t.pins[1].chipId === largestChipId
|
|
7499
|
-
);
|
|
7500
|
-
const host = hostCandidates.length > 0 ? hostCandidates.reduce((a, b) => lengthOf(a) >= lengthOf(b) ? a : b) : this.overlappingSameNetTraceGroup.overlappingTraces;
|
|
7501
|
-
const groupStroke = getColorFromString(groupId, 0.9);
|
|
7502
|
-
const groupFill = getColorFromString(groupId, 0.5);
|
|
7503
|
-
for (const trace of Object.values(this.inputTraceMap)) {
|
|
7504
|
-
if (trace.globalConnNetId !== groupId) continue;
|
|
7505
|
-
const isHost = host ? trace.mspPairId === host.mspPairId : false;
|
|
7506
|
-
graphics.lines.push({
|
|
7507
|
-
points: trace.tracePath,
|
|
7508
|
-
strokeColor: isHost ? groupStroke : groupFill,
|
|
7509
|
-
strokeWidth: isHost ? 6e-3 : 3e-3,
|
|
7510
|
-
strokeDash: isHost ? void 0 : "4 2"
|
|
7511
|
-
});
|
|
7512
|
-
}
|
|
7513
|
-
for (const c of this.testedCandidates) {
|
|
7514
|
-
const fill = c.status === "ok" ? "rgba(0, 180, 0, 0.25)" : c.status === "chip-collision" ? "rgba(220, 0, 0, 0.25)" : c.status === "trace-collision" ? "rgba(220, 140, 0, 0.25)" : "rgba(120, 120, 120, 0.15)";
|
|
7515
|
-
const stroke = c.status === "ok" ? "green" : c.status === "chip-collision" ? "red" : c.status === "trace-collision" ? "orange" : "gray";
|
|
7516
|
-
graphics.rects.push({
|
|
7517
|
-
center: {
|
|
7518
|
-
x: (c.bounds.minX + c.bounds.maxX) / 2,
|
|
7519
|
-
y: (c.bounds.minY + c.bounds.maxY) / 2
|
|
7520
|
-
},
|
|
7521
|
-
width: c.width,
|
|
7522
|
-
height: c.height,
|
|
7523
|
-
fill,
|
|
7524
|
-
strokeColor: stroke
|
|
7525
|
-
});
|
|
7526
|
-
graphics.points.push({
|
|
7527
|
-
x: c.anchor.x,
|
|
7528
|
-
y: c.anchor.y,
|
|
7529
|
-
color: stroke
|
|
7530
|
-
});
|
|
7531
|
-
}
|
|
7532
|
-
if (this.netLabelPlacement) {
|
|
7533
|
-
const p = this.netLabelPlacement;
|
|
7534
|
-
graphics.rects.push({
|
|
7535
|
-
center: p.center,
|
|
7536
|
-
width: p.width,
|
|
7537
|
-
height: p.height,
|
|
7538
|
-
fill: "rgba(0, 128, 255, 0.35)",
|
|
7539
|
-
strokeColor: "blue"
|
|
7540
|
-
});
|
|
7541
|
-
graphics.points.push({
|
|
7542
|
-
x: p.anchorPoint.x,
|
|
7543
|
-
y: p.anchorPoint.y,
|
|
7544
|
-
color: "blue"
|
|
7545
|
-
});
|
|
7546
|
-
}
|
|
7547
|
-
return graphics;
|
|
7672
|
+
return visualizeSingleNetLabelPlacementSolver(this);
|
|
7548
7673
|
}
|
|
7549
7674
|
};
|
|
7550
7675
|
|
|
@@ -7555,6 +7680,8 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
7555
7680
|
overlappingSameNetTraceGroups;
|
|
7556
7681
|
queuedOverlappingSameNetTraceGroups;
|
|
7557
7682
|
netLabelPlacements = [];
|
|
7683
|
+
currentGroup = null;
|
|
7684
|
+
triedAnyOrientationFallbackForCurrentGroup = false;
|
|
7558
7685
|
constructor(params) {
|
|
7559
7686
|
super();
|
|
7560
7687
|
this.inputProblem = params.inputProblem;
|
|
@@ -7672,9 +7799,24 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
7672
7799
|
if (this.activeSubSolver?.solved) {
|
|
7673
7800
|
this.netLabelPlacements.push(this.activeSubSolver.netLabelPlacement);
|
|
7674
7801
|
this.activeSubSolver = null;
|
|
7802
|
+
this.currentGroup = null;
|
|
7803
|
+
this.triedAnyOrientationFallbackForCurrentGroup = false;
|
|
7675
7804
|
return;
|
|
7676
7805
|
}
|
|
7677
7806
|
if (this.activeSubSolver?.failed) {
|
|
7807
|
+
const fullOrients = ["x+", "x-", "y+", "y-"];
|
|
7808
|
+
const currOrients = this.activeSubSolver.availableOrientations;
|
|
7809
|
+
const isAlreadyFull = currOrients.length === 4 && fullOrients.every((o) => currOrients.includes(o));
|
|
7810
|
+
if (!this.triedAnyOrientationFallbackForCurrentGroup && !isAlreadyFull && this.currentGroup) {
|
|
7811
|
+
this.triedAnyOrientationFallbackForCurrentGroup = true;
|
|
7812
|
+
this.activeSubSolver = new SingleNetLabelPlacementSolver({
|
|
7813
|
+
inputProblem: this.inputProblem,
|
|
7814
|
+
inputTraceMap: this.inputTraceMap,
|
|
7815
|
+
overlappingSameNetTraceGroup: this.currentGroup,
|
|
7816
|
+
availableOrientations: fullOrients
|
|
7817
|
+
});
|
|
7818
|
+
return;
|
|
7819
|
+
}
|
|
7678
7820
|
this.failed = true;
|
|
7679
7821
|
this.error = this.activeSubSolver.error;
|
|
7680
7822
|
return;
|
|
@@ -7689,6 +7831,8 @@ var NetLabelPlacementSolver = class extends BaseSolver {
|
|
|
7689
7831
|
return;
|
|
7690
7832
|
}
|
|
7691
7833
|
const netId = nextOverlappingSameNetTraceGroup.netId ?? nextOverlappingSameNetTraceGroup.globalConnNetId;
|
|
7834
|
+
this.currentGroup = nextOverlappingSameNetTraceGroup;
|
|
7835
|
+
this.triedAnyOrientationFallbackForCurrentGroup = false;
|
|
7692
7836
|
this.activeSubSolver = new SingleNetLabelPlacementSolver({
|
|
7693
7837
|
inputProblem: this.inputProblem,
|
|
7694
7838
|
inputTraceMap: this.inputTraceMap,
|