@tscircuit/schematic-trace-solver 0.0.125 → 0.0.127
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/README.md +28 -0
- package/dist/index.d.ts +159 -2
- package/dist/index.js +739 -36
- package/lib/index.ts +2 -0
- package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +424 -0
- package/lib/solvers/InlineNetLabelSolver/getAxisAlignedSegments.ts +73 -0
- package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +15 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +86 -11
- package/lib/solvers/TraceElbowTransitionSimplificationSolver/TraceElbowTransitionSimplificationSolver.ts +216 -0
- package/lib/solvers/TraceElbowTransitionSimplificationSolver/generateElbowTransitionSimplificationCandidates.ts +186 -0
- package/lib/solvers/TraceElbowTransitionSimplificationSolver/types.ts +10 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +3 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +16 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +56 -13
- package/lib/types/InputProblem.ts +24 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260806T093501Z.page.tsx +4 -0
- package/site/examples/inline-net-label01.page.tsx +6 -0
- package/tests/assets/inline-net-label01.json +47 -0
- package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +3 -3
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +6 -6
- package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +135 -0
- package/tests/bug-reports/bug-report-20260806T093501Z/bug-report-20260806T093501Z.json +505 -0
- package/tests/bug-reports/bug-report-20260806T093501Z/bug-report-20260806T093501Z.test.ts +66 -0
- package/tests/examples/__snapshots__/example33.snap.svg +1 -1
- package/tests/functions/getAxisAlignedSegments.test.ts +56 -0
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label01.snap.svg +54 -0
- package/tests/solvers/InlineNetLabelSolver/inline-net-label01.test.ts +44 -0
package/dist/index.js
CHANGED
|
@@ -4000,6 +4000,16 @@ var doesPathOverlapTraceStrokes = (path, traces) => {
|
|
|
4000
4000
|
|
|
4001
4001
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
4002
4002
|
var MAX_TRIES = 5;
|
|
4003
|
+
var PATH_LENGTH_EPSILON = 1e-9;
|
|
4004
|
+
var getPathLength = (points) => {
|
|
4005
|
+
let length = 0;
|
|
4006
|
+
for (let pointIndex = 0; pointIndex < points.length - 1; pointIndex++) {
|
|
4007
|
+
const point = points[pointIndex];
|
|
4008
|
+
const nextPoint = points[pointIndex + 1];
|
|
4009
|
+
length += Math.abs(nextPoint.x - point.x) + Math.abs(nextPoint.y - point.y);
|
|
4010
|
+
}
|
|
4011
|
+
return length;
|
|
4012
|
+
};
|
|
4003
4013
|
var SingleOverlapSolver = class extends BaseSolver {
|
|
4004
4014
|
queuedCandidatePaths;
|
|
4005
4015
|
solvedTracePath = null;
|
|
@@ -4008,32 +4018,47 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
4008
4018
|
obstacles;
|
|
4009
4019
|
label;
|
|
4010
4020
|
tracesToAvoidOverlapping;
|
|
4021
|
+
netLabelPlacements;
|
|
4022
|
+
detourCount;
|
|
4011
4023
|
_tried = 0;
|
|
4012
4024
|
constructor(solverInput) {
|
|
4013
4025
|
super();
|
|
4014
4026
|
this.initialTrace = solverInput.trace;
|
|
4015
4027
|
this.problem = solverInput.problem;
|
|
4016
4028
|
this.label = solverInput.label;
|
|
4029
|
+
this.detourCount = solverInput.detourCount;
|
|
4017
4030
|
this.tracesToAvoidOverlapping = (solverInput.tracesToAvoidOverlapping ?? []).filter((t) => t.globalConnNetId !== solverInput.trace.globalConnNetId);
|
|
4031
|
+
this.netLabelPlacements = solverInput.netLabelPlacements ?? [
|
|
4032
|
+
solverInput.label
|
|
4033
|
+
];
|
|
4034
|
+
this.obstacles = getObstacleRects(this.problem);
|
|
4018
4035
|
const effectivePadding = solverInput.paddingBuffer + solverInput.detourCount * solverInput.paddingBuffer;
|
|
4019
4036
|
const candidates = generateRerouteCandidates({
|
|
4020
4037
|
...solverInput,
|
|
4021
4038
|
paddingBuffer: effectivePadding
|
|
4022
4039
|
// Use the calculated, larger padding
|
|
4023
4040
|
});
|
|
4024
|
-
const
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
this.
|
|
4041
|
+
const getLabelOverlapCount = (path) => detectTraceLabelOverlap({
|
|
4042
|
+
traces: [{ ...this.initialTrace, tracePath: path }],
|
|
4043
|
+
netLabels: this.netLabelPlacements
|
|
4044
|
+
}).length;
|
|
4045
|
+
const candidateByPath = /* @__PURE__ */ new Map();
|
|
4046
|
+
for (const candidate of candidates) {
|
|
4047
|
+
const simplifiedCandidate = simplifyPath(candidate);
|
|
4048
|
+
candidateByPath.set(
|
|
4049
|
+
simplifiedCandidate.map((point) => `${point.x},${point.y}`).join(";"),
|
|
4050
|
+
simplifiedCandidate
|
|
4051
|
+
);
|
|
4052
|
+
}
|
|
4053
|
+
this.queuedCandidatePaths = [...candidateByPath.values()].sort((a, b) => {
|
|
4054
|
+
const pathLengthDifference = getPathLength(a) - getPathLength(b);
|
|
4055
|
+
if (Math.abs(pathLengthDifference) >= PATH_LENGTH_EPSILON) {
|
|
4056
|
+
return pathLengthDifference;
|
|
4057
|
+
}
|
|
4058
|
+
const overlapCountDifference = getLabelOverlapCount(a) - getLabelOverlapCount(b);
|
|
4059
|
+
if (overlapCountDifference !== 0) return overlapCountDifference;
|
|
4060
|
+
return a.length - b.length;
|
|
4061
|
+
});
|
|
4037
4062
|
}
|
|
4038
4063
|
_step() {
|
|
4039
4064
|
if (this.queuedCandidatePaths.length === 0 || this._tried >= MAX_TRIES) {
|
|
@@ -4047,7 +4072,16 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
4047
4072
|
traces: [{ ...this.initialTrace, tracePath: simplifiedPath }],
|
|
4048
4073
|
netLabels: [this.label]
|
|
4049
4074
|
}).length > 0;
|
|
4050
|
-
|
|
4075
|
+
const initialPath = simplifyPath(this.initialTrace.tracePath);
|
|
4076
|
+
const initialLabelOverlaps = detectTraceLabelOverlap({
|
|
4077
|
+
traces: [{ ...this.initialTrace, tracePath: initialPath }],
|
|
4078
|
+
netLabels: this.netLabelPlacements
|
|
4079
|
+
});
|
|
4080
|
+
const candidateLabelOverlaps = detectTraceLabelOverlap({
|
|
4081
|
+
traces: [{ ...this.initialTrace, tracePath: simplifiedPath }],
|
|
4082
|
+
netLabels: this.netLabelPlacements
|
|
4083
|
+
});
|
|
4084
|
+
if (!stillOverlapsLabel && candidateLabelOverlaps.length <= initialLabelOverlaps.length && !isPathCollidingWithObstacles(simplifiedPath, this.obstacles) && !doesPathCoincideWithTraces(simplifiedPath, this.tracesToAvoidOverlapping)) {
|
|
4051
4085
|
this.solvedTracePath = simplifiedPath;
|
|
4052
4086
|
this.solved = true;
|
|
4053
4087
|
}
|
|
@@ -4070,7 +4104,7 @@ var SingleOverlapSolver = class extends BaseSolver {
|
|
|
4070
4104
|
height: this.label.height,
|
|
4071
4105
|
fill: "rgba(255, 0, 0, 0.2)"
|
|
4072
4106
|
});
|
|
4073
|
-
if (this.queuedCandidatePaths.length > 0) {
|
|
4107
|
+
if (!this.solvedTracePath && this.queuedCandidatePaths.length > 0) {
|
|
4074
4108
|
graphics.lines.push({
|
|
4075
4109
|
points: this.queuedCandidatePaths[0],
|
|
4076
4110
|
strokeColor: "orange"
|
|
@@ -4154,6 +4188,7 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
4154
4188
|
allTraces;
|
|
4155
4189
|
tracesToAvoidOverlapping;
|
|
4156
4190
|
modifiedTraces = [];
|
|
4191
|
+
completedReroutes = [];
|
|
4157
4192
|
PADDING_BUFFER = 0.1;
|
|
4158
4193
|
detourCounts = /* @__PURE__ */ new Map();
|
|
4159
4194
|
activeSubSolver = null;
|
|
@@ -4179,6 +4214,17 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
4179
4214
|
if (this.activeSubSolver.solved) {
|
|
4180
4215
|
const solvedPath = this.activeSubSolver.solvedTracePath;
|
|
4181
4216
|
if (solvedPath) {
|
|
4217
|
+
this.completedReroutes.push({
|
|
4218
|
+
initialTrace: {
|
|
4219
|
+
...this.activeSubSolver.initialTrace,
|
|
4220
|
+
tracePath: this.activeSubSolver.initialTrace.tracePath.map(
|
|
4221
|
+
(point) => ({ ...point })
|
|
4222
|
+
)
|
|
4223
|
+
},
|
|
4224
|
+
reroutedTracePath: solvedPath.map((point) => ({ ...point })),
|
|
4225
|
+
label: this.activeSubSolver.label,
|
|
4226
|
+
detourCount: this.activeSubSolver.detourCount
|
|
4227
|
+
});
|
|
4182
4228
|
const traceIndex = this.allTraces.findIndex(
|
|
4183
4229
|
(t) => t.mspPairId === this.activeSubSolver.initialTrace.mspPairId
|
|
4184
4230
|
);
|
|
@@ -4251,7 +4297,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
4251
4297
|
problem: this.inputProblem,
|
|
4252
4298
|
paddingBuffer: this.PADDING_BUFFER,
|
|
4253
4299
|
detourCount: detourCount2,
|
|
4254
|
-
tracesToAvoidOverlapping: this.tracesToAvoidOverlapping
|
|
4300
|
+
tracesToAvoidOverlapping: this.tracesToAvoidOverlapping,
|
|
4301
|
+
netLabelPlacements: this.initialNetLabelPlacements
|
|
4255
4302
|
});
|
|
4256
4303
|
} else {
|
|
4257
4304
|
const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`;
|
|
@@ -4287,7 +4334,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
4287
4334
|
problem: this.inputProblem,
|
|
4288
4335
|
paddingBuffer: this.PADDING_BUFFER,
|
|
4289
4336
|
detourCount: detourCount2,
|
|
4290
|
-
tracesToAvoidOverlapping: this.tracesToAvoidOverlapping
|
|
4337
|
+
tracesToAvoidOverlapping: this.tracesToAvoidOverlapping,
|
|
4338
|
+
netLabelPlacements: this.initialNetLabelPlacements
|
|
4291
4339
|
});
|
|
4292
4340
|
} else {
|
|
4293
4341
|
const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`;
|
|
@@ -4303,7 +4351,8 @@ var OverlapAvoidanceStepSolver = class extends BaseSolver {
|
|
|
4303
4351
|
problem: this.inputProblem,
|
|
4304
4352
|
paddingBuffer: this.PADDING_BUFFER,
|
|
4305
4353
|
detourCount,
|
|
4306
|
-
tracesToAvoidOverlapping: this.tracesToAvoidOverlapping
|
|
4354
|
+
tracesToAvoidOverlapping: this.tracesToAvoidOverlapping,
|
|
4355
|
+
netLabelPlacements: this.initialNetLabelPlacements
|
|
4307
4356
|
});
|
|
4308
4357
|
}
|
|
4309
4358
|
}
|
|
@@ -4440,6 +4489,9 @@ var TraceLabelOverlapAvoidanceSolver = class extends BaseSolver {
|
|
|
4440
4489
|
const solvedTraces = this.subSolvers.flatMap((s) => s.getOutput().allTraces);
|
|
4441
4490
|
return {
|
|
4442
4491
|
traces: [...this.cleanTraces, ...solvedTraces],
|
|
4492
|
+
completedReroutes: this.subSolvers.flatMap(
|
|
4493
|
+
(solver) => solver.completedReroutes
|
|
4494
|
+
),
|
|
4443
4495
|
netLabelPlacements: this.labelMergingSolver?.getOutput().netLabelPlacements ?? this.netLabelPlacements
|
|
4444
4496
|
};
|
|
4445
4497
|
}
|
|
@@ -5527,7 +5579,7 @@ var segmentRunsAlongRectBoundary = (start, end, rect) => {
|
|
|
5527
5579
|
|
|
5528
5580
|
// lib/solvers/Example28Solver/geometry.ts
|
|
5529
5581
|
var getPathKey = (path) => path.map((point) => `${point.x},${point.y}`).join(";");
|
|
5530
|
-
var
|
|
5582
|
+
var getPathLength2 = (path) => {
|
|
5531
5583
|
let length = 0;
|
|
5532
5584
|
for (let i = 0; i < path.length - 1; i++) {
|
|
5533
5585
|
length += Math.abs(path[i + 1].x - path[i].x) + Math.abs(path[i + 1].y - path[i].y);
|
|
@@ -5681,7 +5733,7 @@ var getTraceGeometryMetrics = (traces, allTraces) => ({
|
|
|
5681
5733
|
),
|
|
5682
5734
|
visibleLength: getVisibleTraceLength(traces),
|
|
5683
5735
|
pathLength: traces.reduce(
|
|
5684
|
-
(sum, trace) => sum +
|
|
5736
|
+
(sum, trace) => sum + getPathLength2(trace.tracePath),
|
|
5685
5737
|
0
|
|
5686
5738
|
),
|
|
5687
5739
|
otherNetCrossings: countOtherNetCrossings(traces, allTraces)
|
|
@@ -6496,7 +6548,7 @@ var UntangleTraceSubsolver = class extends BaseSolver {
|
|
|
6496
6548
|
candidate.traceId
|
|
6497
6549
|
).isColliding && (crossing.isInitialBundleCrossing || !candidate.collision.isColliding)
|
|
6498
6550
|
).sort(
|
|
6499
|
-
(first, second) => Number(first.collision.isColliding) - Number(second.collision.isColliding) ||
|
|
6551
|
+
(first, second) => Number(first.collision.isColliding) - Number(second.collision.isColliding) || getPathLength2(first.path) - getPathLength2(second.path)
|
|
6500
6552
|
);
|
|
6501
6553
|
const bestCandidate = validCandidates[0];
|
|
6502
6554
|
if (!bestCandidate) return false;
|
|
@@ -7405,7 +7457,7 @@ var scoreTracePath = ({
|
|
|
7405
7457
|
labelHugDistance: getLabelHugDistance(tracePath, obstacleLabel),
|
|
7406
7458
|
traceIntersections: countTraceIntersections(candidateTrace, outputTraces),
|
|
7407
7459
|
chipBoundaryOverlap: getChipBoundaryOverlap(tracePath, chipObstacles),
|
|
7408
|
-
pathLength:
|
|
7460
|
+
pathLength: getPathLength2(tracePath)
|
|
7409
7461
|
};
|
|
7410
7462
|
};
|
|
7411
7463
|
var countTraceIntersections = (trace, outputTraces) => {
|
|
@@ -10297,6 +10349,7 @@ var NetLabelTraceCollisionSolver = class extends BaseSolver {
|
|
|
10297
10349
|
netLabelPlacements;
|
|
10298
10350
|
outputTraces;
|
|
10299
10351
|
outputNetLabelPlacements;
|
|
10352
|
+
completedReroutes = [];
|
|
10300
10353
|
activeSubSolver = null;
|
|
10301
10354
|
recentlyFailed = /* @__PURE__ */ new Set();
|
|
10302
10355
|
detourCounts = /* @__PURE__ */ new Map();
|
|
@@ -10322,6 +10375,17 @@ var NetLabelTraceCollisionSolver = class extends BaseSolver {
|
|
|
10322
10375
|
if (this.activeSubSolver.solved) {
|
|
10323
10376
|
const solvedPath = this.activeSubSolver.solvedTracePath;
|
|
10324
10377
|
if (solvedPath) {
|
|
10378
|
+
this.completedReroutes.push({
|
|
10379
|
+
initialTrace: {
|
|
10380
|
+
...this.activeSubSolver.initialTrace,
|
|
10381
|
+
tracePath: this.activeSubSolver.initialTrace.tracePath.map(
|
|
10382
|
+
(point) => ({ ...point })
|
|
10383
|
+
)
|
|
10384
|
+
},
|
|
10385
|
+
reroutedTracePath: solvedPath.map((point) => ({ ...point })),
|
|
10386
|
+
label: this.activeSubSolver.label,
|
|
10387
|
+
detourCount: this.activeSubSolver.detourCount
|
|
10388
|
+
});
|
|
10325
10389
|
const idx = this.outputTraces.findIndex(
|
|
10326
10390
|
(t) => t.mspPairId === this.activeSubSolver.initialTrace.mspPairId
|
|
10327
10391
|
);
|
|
@@ -10384,7 +10448,8 @@ var NetLabelTraceCollisionSolver = class extends BaseSolver {
|
|
|
10384
10448
|
problem: this.inputProblem,
|
|
10385
10449
|
paddingBuffer: PADDING_BUFFER,
|
|
10386
10450
|
detourCount,
|
|
10387
|
-
tracesToAvoidOverlapping: this.outputTraces
|
|
10451
|
+
tracesToAvoidOverlapping: this.outputTraces,
|
|
10452
|
+
netLabelPlacements: this.outputNetLabelPlacements
|
|
10388
10453
|
});
|
|
10389
10454
|
}
|
|
10390
10455
|
/**
|
|
@@ -10416,6 +10481,7 @@ var NetLabelTraceCollisionSolver = class extends BaseSolver {
|
|
|
10416
10481
|
getOutput() {
|
|
10417
10482
|
return {
|
|
10418
10483
|
traces: this.outputTraces,
|
|
10484
|
+
completedReroutes: this.completedReroutes,
|
|
10419
10485
|
netLabelPlacements: this.outputNetLabelPlacements
|
|
10420
10486
|
};
|
|
10421
10487
|
}
|
|
@@ -10828,7 +10894,7 @@ var removeConsecutiveDuplicatePoints = (path) => {
|
|
|
10828
10894
|
}
|
|
10829
10895
|
return filteredPath;
|
|
10830
10896
|
};
|
|
10831
|
-
var
|
|
10897
|
+
var getPathLength3 = (path) => {
|
|
10832
10898
|
let pathLength = 0;
|
|
10833
10899
|
for (let pointIndex = 0; pointIndex < path.length - 1; pointIndex++) {
|
|
10834
10900
|
const startPoint = path[pointIndex];
|
|
@@ -10912,7 +10978,7 @@ var getPerimeterCandidates = ({
|
|
|
10912
10978
|
);
|
|
10913
10979
|
}
|
|
10914
10980
|
return candidates.sort(
|
|
10915
|
-
(firstPath, secondPath) =>
|
|
10981
|
+
(firstPath, secondPath) => getPathLength3(firstPath) - getPathLength3(secondPath)
|
|
10916
10982
|
);
|
|
10917
10983
|
};
|
|
10918
10984
|
var getSegmentMidpoint = (startPoint, endPoint) => {
|
|
@@ -11017,7 +11083,7 @@ var getJunctionCandidates = ({
|
|
|
11017
11083
|
}
|
|
11018
11084
|
}
|
|
11019
11085
|
return candidates.sort(
|
|
11020
|
-
(firstPath, secondPath) =>
|
|
11086
|
+
(firstPath, secondPath) => getPathLength3(firstPath) - getPathLength3(secondPath)
|
|
11021
11087
|
);
|
|
11022
11088
|
};
|
|
11023
11089
|
var pathCollidesWithObstacles = ({
|
|
@@ -11525,6 +11591,569 @@ orientation: ${label.orientation}`
|
|
|
11525
11591
|
}
|
|
11526
11592
|
};
|
|
11527
11593
|
|
|
11594
|
+
// lib/solvers/TraceElbowTransitionSimplificationSolver/generateElbowTransitionSimplificationCandidates.ts
|
|
11595
|
+
var isSimpleFiveSegmentElbow = (path) => {
|
|
11596
|
+
const simplifiedPath = simplifyPath(path);
|
|
11597
|
+
if (simplifiedPath.length !== 6) return false;
|
|
11598
|
+
const segmentIsHorizontal = simplifiedPath.slice(0, -1).map((point, index) => isHorizontal(point, simplifiedPath[index + 1]));
|
|
11599
|
+
return segmentIsHorizontal.every(
|
|
11600
|
+
(isHorizontalSegment, index) => index === 0 || isHorizontalSegment !== segmentIsHorizontal[index - 1]
|
|
11601
|
+
);
|
|
11602
|
+
};
|
|
11603
|
+
var generateSegmentShiftCandidates = ({
|
|
11604
|
+
trace,
|
|
11605
|
+
label,
|
|
11606
|
+
paddingBuffer,
|
|
11607
|
+
detourCount
|
|
11608
|
+
}) => {
|
|
11609
|
+
if (trace.globalConnNetId === label.globalConnNetId) return [];
|
|
11610
|
+
const path = simplifyPath(trace.tracePath);
|
|
11611
|
+
if (!isSimpleFiveSegmentElbow(path)) return [];
|
|
11612
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
11613
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
11614
|
+
const paddedLabelBounds = {
|
|
11615
|
+
minX: labelBounds.minX - effectivePadding,
|
|
11616
|
+
maxX: labelBounds.maxX + effectivePadding,
|
|
11617
|
+
minY: labelBounds.minY - effectivePadding,
|
|
11618
|
+
maxY: labelBounds.maxY + effectivePadding
|
|
11619
|
+
};
|
|
11620
|
+
const candidates = [];
|
|
11621
|
+
for (let segmentIndex = 1; segmentIndex < path.length - 2; segmentIndex++) {
|
|
11622
|
+
const segmentStart = path[segmentIndex];
|
|
11623
|
+
const segmentEnd = path[segmentIndex + 1];
|
|
11624
|
+
if (!segmentIntersectsRect(segmentStart, segmentEnd, labelBounds)) continue;
|
|
11625
|
+
const isHorizontalSegment = isHorizontal(segmentStart, segmentEnd);
|
|
11626
|
+
const isVerticalSegment = isVertical(segmentStart, segmentEnd);
|
|
11627
|
+
if (!isHorizontalSegment && !isVerticalSegment) continue;
|
|
11628
|
+
const axis = isHorizontalSegment ? "y" : "x";
|
|
11629
|
+
const coordinates = isHorizontalSegment ? [paddedLabelBounds.minY, paddedLabelBounds.maxY] : [paddedLabelBounds.minX, paddedLabelBounds.maxX];
|
|
11630
|
+
for (const coordinate of coordinates) {
|
|
11631
|
+
const shiftedPath = shiftSegmentOrth(path, segmentIndex, axis, coordinate);
|
|
11632
|
+
if (shiftedPath) candidates.push(shiftedPath);
|
|
11633
|
+
}
|
|
11634
|
+
}
|
|
11635
|
+
return candidates;
|
|
11636
|
+
};
|
|
11637
|
+
var generateTransitionShiftCandidates = ({
|
|
11638
|
+
trace,
|
|
11639
|
+
label,
|
|
11640
|
+
paddingBuffer,
|
|
11641
|
+
detourCount
|
|
11642
|
+
}) => {
|
|
11643
|
+
const path = simplifyPath(trace.tracePath);
|
|
11644
|
+
if (!isSimpleFiveSegmentElbow(path)) return [];
|
|
11645
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height);
|
|
11646
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer;
|
|
11647
|
+
const paddedLabelBounds = {
|
|
11648
|
+
minX: labelBounds.minX - effectivePadding,
|
|
11649
|
+
maxX: labelBounds.maxX + effectivePadding,
|
|
11650
|
+
minY: labelBounds.minY - effectivePadding,
|
|
11651
|
+
maxY: labelBounds.maxY + effectivePadding
|
|
11652
|
+
};
|
|
11653
|
+
const start = path[0];
|
|
11654
|
+
const end = path[path.length - 1];
|
|
11655
|
+
const middleSegmentIndex = 2;
|
|
11656
|
+
const candidates = [];
|
|
11657
|
+
for (let segmentIndex = 0; segmentIndex < path.length - 1; segmentIndex++) {
|
|
11658
|
+
const segmentStart = path[segmentIndex];
|
|
11659
|
+
const segmentEnd = path[segmentIndex + 1];
|
|
11660
|
+
if (!segmentIntersectsRect(segmentStart, segmentEnd, labelBounds)) continue;
|
|
11661
|
+
if (Math.abs(segmentIndex - middleSegmentIndex) !== 1) continue;
|
|
11662
|
+
const isHorizontalSegment = isHorizontal(segmentStart, segmentEnd);
|
|
11663
|
+
const isVerticalSegment = isVertical(segmentStart, segmentEnd);
|
|
11664
|
+
if (!isHorizontalSegment && !isVerticalSegment) continue;
|
|
11665
|
+
const axis = isHorizontalSegment ? "x" : "y";
|
|
11666
|
+
const min = isHorizontalSegment ? paddedLabelBounds.minX : paddedLabelBounds.minY;
|
|
11667
|
+
const max = isHorizontalSegment ? paddedLabelBounds.maxX : paddedLabelBounds.maxY;
|
|
11668
|
+
const startCoordinate = isHorizontalSegment ? start.x : start.y;
|
|
11669
|
+
const endCoordinate = isHorizontalSegment ? end.x : end.y;
|
|
11670
|
+
const corridorCoordinates = [];
|
|
11671
|
+
if (startCoordinate < min) {
|
|
11672
|
+
corridorCoordinates.push((startCoordinate + min) / 2);
|
|
11673
|
+
} else if (startCoordinate > max) {
|
|
11674
|
+
corridorCoordinates.push((startCoordinate + max) / 2);
|
|
11675
|
+
}
|
|
11676
|
+
if (endCoordinate < min) {
|
|
11677
|
+
corridorCoordinates.push((endCoordinate + min) / 2);
|
|
11678
|
+
} else if (endCoordinate > max) {
|
|
11679
|
+
corridorCoordinates.push((endCoordinate + max) / 2);
|
|
11680
|
+
}
|
|
11681
|
+
for (const coordinate of new Set(corridorCoordinates)) {
|
|
11682
|
+
const shiftedPath = shiftSegmentOrth(
|
|
11683
|
+
path,
|
|
11684
|
+
middleSegmentIndex,
|
|
11685
|
+
axis,
|
|
11686
|
+
coordinate
|
|
11687
|
+
);
|
|
11688
|
+
if (shiftedPath) candidates.push(shiftedPath);
|
|
11689
|
+
}
|
|
11690
|
+
}
|
|
11691
|
+
return candidates;
|
|
11692
|
+
};
|
|
11693
|
+
var generateElbowTransitionSimplificationCandidates = ({
|
|
11694
|
+
trace,
|
|
11695
|
+
label,
|
|
11696
|
+
netLabelPlacements,
|
|
11697
|
+
paddingBuffer,
|
|
11698
|
+
detourCount
|
|
11699
|
+
}) => {
|
|
11700
|
+
const transitionCandidates = generateTransitionShiftCandidates({
|
|
11701
|
+
trace,
|
|
11702
|
+
label,
|
|
11703
|
+
paddingBuffer,
|
|
11704
|
+
detourCount
|
|
11705
|
+
});
|
|
11706
|
+
return transitionCandidates.flatMap((tracePath) => {
|
|
11707
|
+
const shiftedTrace = { ...trace, tracePath };
|
|
11708
|
+
const shiftedOverlaps = detectTraceLabelOverlap({
|
|
11709
|
+
traces: [shiftedTrace],
|
|
11710
|
+
netLabels: netLabelPlacements
|
|
11711
|
+
});
|
|
11712
|
+
return shiftedOverlaps.flatMap(
|
|
11713
|
+
({ label: shiftedLabel }) => generateSegmentShiftCandidates({
|
|
11714
|
+
trace: shiftedTrace,
|
|
11715
|
+
label: shiftedLabel,
|
|
11716
|
+
paddingBuffer,
|
|
11717
|
+
detourCount
|
|
11718
|
+
})
|
|
11719
|
+
);
|
|
11720
|
+
});
|
|
11721
|
+
};
|
|
11722
|
+
|
|
11723
|
+
// lib/solvers/TraceElbowTransitionSimplificationSolver/TraceElbowTransitionSimplificationSolver.ts
|
|
11724
|
+
var PATH_LENGTH_EPSILON2 = 1e-9;
|
|
11725
|
+
var getPathLength4 = (points) => points.slice(1).reduce((length, point, pointIndex) => {
|
|
11726
|
+
const previousPoint = points[pointIndex];
|
|
11727
|
+
return length + Math.abs(point.x - previousPoint.x) + Math.abs(point.y - previousPoint.y);
|
|
11728
|
+
}, 0);
|
|
11729
|
+
var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
|
|
11730
|
+
input;
|
|
11731
|
+
outputTraces;
|
|
11732
|
+
traceIdQueue;
|
|
11733
|
+
obstacles;
|
|
11734
|
+
constructor(input) {
|
|
11735
|
+
super();
|
|
11736
|
+
this.input = input;
|
|
11737
|
+
this.outputTraces = [...input.traces];
|
|
11738
|
+
const reroutedTraceIds = new Set(
|
|
11739
|
+
input.completedReroutes.map(
|
|
11740
|
+
(completedReroute) => completedReroute.initialTrace.mspPairId
|
|
11741
|
+
)
|
|
11742
|
+
);
|
|
11743
|
+
this.traceIdQueue = input.traces.map((trace) => trace.mspPairId).filter((traceId) => reroutedTraceIds.has(traceId));
|
|
11744
|
+
this.obstacles = getObstacleRects(input.inputProblem);
|
|
11745
|
+
}
|
|
11746
|
+
_step() {
|
|
11747
|
+
const traceId = this.traceIdQueue.shift();
|
|
11748
|
+
if (!traceId) {
|
|
11749
|
+
this.solved = true;
|
|
11750
|
+
return;
|
|
11751
|
+
}
|
|
11752
|
+
const traceIndex = this.outputTraces.findIndex(
|
|
11753
|
+
(trace2) => trace2.mspPairId === traceId
|
|
11754
|
+
);
|
|
11755
|
+
const trace = this.outputTraces[traceIndex];
|
|
11756
|
+
const tracePath = simplifyPath(trace.tracePath);
|
|
11757
|
+
const initialOverlaps = detectTraceLabelOverlap({
|
|
11758
|
+
traces: [{ ...trace, tracePath }],
|
|
11759
|
+
netLabels: this.input.netLabelPlacements
|
|
11760
|
+
});
|
|
11761
|
+
const otherNetTraces = this.outputTraces.filter(
|
|
11762
|
+
(otherTrace) => otherTrace.mspPairId !== trace.mspPairId && otherTrace.globalConnNetId !== trace.globalConnNetId
|
|
11763
|
+
);
|
|
11764
|
+
const candidateByPath = /* @__PURE__ */ new Map();
|
|
11765
|
+
const completedReroutes = this.input.completedReroutes.filter(
|
|
11766
|
+
(completedReroute) => completedReroute.initialTrace.mspPairId === trace.mspPairId
|
|
11767
|
+
);
|
|
11768
|
+
for (const completedReroute of completedReroutes) {
|
|
11769
|
+
const initialReroutePath = simplifyPath(
|
|
11770
|
+
completedReroute.initialTrace.tracePath
|
|
11771
|
+
);
|
|
11772
|
+
const reroutedPath = simplifyPath(completedReroute.reroutedTracePath);
|
|
11773
|
+
const initialRerouteOverlapCount = detectTraceLabelOverlap({
|
|
11774
|
+
traces: [
|
|
11775
|
+
{ ...completedReroute.initialTrace, tracePath: initialReroutePath }
|
|
11776
|
+
],
|
|
11777
|
+
netLabels: this.input.netLabelPlacements
|
|
11778
|
+
}).length;
|
|
11779
|
+
const candidates = generateElbowTransitionSimplificationCandidates({
|
|
11780
|
+
trace: completedReroute.initialTrace,
|
|
11781
|
+
label: completedReroute.label,
|
|
11782
|
+
netLabelPlacements: this.input.netLabelPlacements,
|
|
11783
|
+
paddingBuffer: this.input.paddingBuffer,
|
|
11784
|
+
detourCount: completedReroute.detourCount
|
|
11785
|
+
});
|
|
11786
|
+
for (const candidate of candidates) {
|
|
11787
|
+
const simplifiedCandidate = simplifyPath(candidate);
|
|
11788
|
+
const candidateTrace = {
|
|
11789
|
+
...completedReroute.initialTrace,
|
|
11790
|
+
tracePath: simplifiedCandidate
|
|
11791
|
+
};
|
|
11792
|
+
const candidateOverlapCount = detectTraceLabelOverlap({
|
|
11793
|
+
traces: [candidateTrace],
|
|
11794
|
+
netLabels: this.input.netLabelPlacements
|
|
11795
|
+
}).length;
|
|
11796
|
+
const isSimplerEquivalentReroute = candidateOverlapCount < initialRerouteOverlapCount && Math.abs(
|
|
11797
|
+
getPathLength4(simplifiedCandidate) - getPathLength4(reroutedPath)
|
|
11798
|
+
) < PATH_LENGTH_EPSILON2 && simplifiedCandidate.length < reroutedPath.length && preservesLabelAnchors(
|
|
11799
|
+
this.input.netLabelPlacements,
|
|
11800
|
+
[completedReroute.initialTrace],
|
|
11801
|
+
[candidateTrace]
|
|
11802
|
+
);
|
|
11803
|
+
if (isSimplerEquivalentReroute) {
|
|
11804
|
+
candidateByPath.set(
|
|
11805
|
+
simplifiedCandidate.map((point) => `${point.x},${point.y}`).join(";"),
|
|
11806
|
+
simplifiedCandidate
|
|
11807
|
+
);
|
|
11808
|
+
}
|
|
11809
|
+
}
|
|
11810
|
+
}
|
|
11811
|
+
const initialOverlapIds = new Set(
|
|
11812
|
+
initialOverlaps.map(
|
|
11813
|
+
({ label }) => `${label.globalConnNetId}:${label.netId}`
|
|
11814
|
+
)
|
|
11815
|
+
);
|
|
11816
|
+
const initialPathLength = getPathLength4(tracePath);
|
|
11817
|
+
const validCandidates = [...candidateByPath.values()].filter(
|
|
11818
|
+
(candidatePath) => {
|
|
11819
|
+
const candidateTrace = { ...trace, tracePath: candidatePath };
|
|
11820
|
+
const candidateOverlaps = detectTraceLabelOverlap({
|
|
11821
|
+
traces: [candidateTrace],
|
|
11822
|
+
netLabels: this.input.netLabelPlacements
|
|
11823
|
+
});
|
|
11824
|
+
const candidateOnlyKeepsExistingOverlaps = candidateOverlaps.every(
|
|
11825
|
+
({ label }) => initialOverlapIds.has(`${label.globalConnNetId}:${label.netId}`)
|
|
11826
|
+
);
|
|
11827
|
+
const reducesCollisions = candidateOverlaps.length < initialOverlaps.length;
|
|
11828
|
+
const simplifiesGeometry = candidateOverlaps.length === initialOverlaps.length && getPathLength4(candidatePath) <= initialPathLength + PATH_LENGTH_EPSILON2 && candidatePath.length < tracePath.length;
|
|
11829
|
+
return candidateOnlyKeepsExistingOverlaps && (reducesCollisions || simplifiesGeometry) && preservesLabelAnchors(
|
|
11830
|
+
this.input.netLabelPlacements,
|
|
11831
|
+
[trace],
|
|
11832
|
+
[candidateTrace]
|
|
11833
|
+
) && !isPathCollidingWithObstacles(candidatePath, this.obstacles) && !doesPathCoincideWithTraces(candidatePath, otherNetTraces);
|
|
11834
|
+
}
|
|
11835
|
+
);
|
|
11836
|
+
const getOverlapCount = (candidatePath) => detectTraceLabelOverlap({
|
|
11837
|
+
traces: [{ ...trace, tracePath: candidatePath }],
|
|
11838
|
+
netLabels: this.input.netLabelPlacements
|
|
11839
|
+
}).length;
|
|
11840
|
+
validCandidates.sort((a, b) => {
|
|
11841
|
+
const overlapDifference = getOverlapCount(a) - getOverlapCount(b);
|
|
11842
|
+
if (overlapDifference !== 0) return overlapDifference;
|
|
11843
|
+
return getPathLength4(a) - getPathLength4(b) || a.length - b.length;
|
|
11844
|
+
});
|
|
11845
|
+
const bestCandidate = validCandidates[0];
|
|
11846
|
+
if (!bestCandidate) return;
|
|
11847
|
+
this.outputTraces[traceIndex] = { ...trace, tracePath: bestCandidate };
|
|
11848
|
+
this.stats.simplifiedTraceCount = (this.stats.simplifiedTraceCount ?? 0) + 1;
|
|
11849
|
+
}
|
|
11850
|
+
getOutput() {
|
|
11851
|
+
return {
|
|
11852
|
+
traces: this.outputTraces,
|
|
11853
|
+
netLabelPlacements: this.input.netLabelPlacements
|
|
11854
|
+
};
|
|
11855
|
+
}
|
|
11856
|
+
visualize() {
|
|
11857
|
+
const graphics = visualizeInputProblem(this.input.inputProblem);
|
|
11858
|
+
graphics.lines ??= [];
|
|
11859
|
+
for (const trace of this.outputTraces) {
|
|
11860
|
+
graphics.lines.push({ points: trace.tracePath, strokeColor: "purple" });
|
|
11861
|
+
}
|
|
11862
|
+
return graphics;
|
|
11863
|
+
}
|
|
11864
|
+
};
|
|
11865
|
+
|
|
11866
|
+
// lib/solvers/InlineNetLabelSolver/getAxisAlignedSegments.ts
|
|
11867
|
+
var getAxisAlignedSegments = (path, epsilon = 1e-6) => {
|
|
11868
|
+
const segments = [];
|
|
11869
|
+
let runStart = null;
|
|
11870
|
+
let runAxis = null;
|
|
11871
|
+
let runSign = 0;
|
|
11872
|
+
const closeRun = (runEnd) => {
|
|
11873
|
+
if (runStart && runAxis) {
|
|
11874
|
+
const length = runAxis === "x" ? Math.abs(runEnd.x - runStart.x) : Math.abs(runEnd.y - runStart.y);
|
|
11875
|
+
if (length > epsilon) {
|
|
11876
|
+
segments.push({ start: runStart, end: runEnd, axis: runAxis, length });
|
|
11877
|
+
}
|
|
11878
|
+
}
|
|
11879
|
+
runStart = null;
|
|
11880
|
+
runAxis = null;
|
|
11881
|
+
runSign = 0;
|
|
11882
|
+
};
|
|
11883
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
11884
|
+
const a = path[i];
|
|
11885
|
+
const b = path[i + 1];
|
|
11886
|
+
const dx = b.x - a.x;
|
|
11887
|
+
const dy = b.y - a.y;
|
|
11888
|
+
if (Math.abs(dx) <= epsilon && Math.abs(dy) <= epsilon) continue;
|
|
11889
|
+
const axis = Math.abs(dy) <= epsilon ? "x" : Math.abs(dx) <= epsilon ? "y" : null;
|
|
11890
|
+
if (!axis) {
|
|
11891
|
+
closeRun(a);
|
|
11892
|
+
continue;
|
|
11893
|
+
}
|
|
11894
|
+
const sign = Math.sign(axis === "x" ? dx : dy);
|
|
11895
|
+
if (runStart && runAxis === axis && runSign === sign) continue;
|
|
11896
|
+
closeRun(a);
|
|
11897
|
+
runStart = a;
|
|
11898
|
+
runAxis = axis;
|
|
11899
|
+
runSign = sign;
|
|
11900
|
+
}
|
|
11901
|
+
if (path.length >= 2) {
|
|
11902
|
+
closeRun(path[path.length - 1]);
|
|
11903
|
+
}
|
|
11904
|
+
return segments.sort((a, b) => b.length - a.length);
|
|
11905
|
+
};
|
|
11906
|
+
|
|
11907
|
+
// lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts
|
|
11908
|
+
var DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
|
|
11909
|
+
var INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
|
|
11910
|
+
var MIN_INLINE_NET_LABEL_SEGMENT_RATIO = 0.5;
|
|
11911
|
+
var getPinPairKey2 = (pinIds) => [...pinIds].sort().join("::");
|
|
11912
|
+
var InlineNetLabelSolver = class extends BaseSolver {
|
|
11913
|
+
inputProblem;
|
|
11914
|
+
traces;
|
|
11915
|
+
inputNetLabelPlacements;
|
|
11916
|
+
inlineNetLabelPlacements = [];
|
|
11917
|
+
/** Direct connections that opted in, still waiting to be processed */
|
|
11918
|
+
queuedDirectConnections;
|
|
11919
|
+
tracesByPinPairKey;
|
|
11920
|
+
constructor(input) {
|
|
11921
|
+
super();
|
|
11922
|
+
this.inputProblem = input.inputProblem;
|
|
11923
|
+
this.traces = input.traces;
|
|
11924
|
+
this.inputNetLabelPlacements = input.netLabelPlacements;
|
|
11925
|
+
this.queuedDirectConnections = this.inputProblem.directConnections.filter(
|
|
11926
|
+
(dc) => dc.allowInlineNetLabel && dc.netId
|
|
11927
|
+
);
|
|
11928
|
+
this.tracesByPinPairKey = /* @__PURE__ */ new Map();
|
|
11929
|
+
for (const trace of this.traces) {
|
|
11930
|
+
const key = getPinPairKey2(trace.pins.map((p) => p.pinId));
|
|
11931
|
+
const existing = this.tracesByPinPairKey.get(key);
|
|
11932
|
+
if (existing) {
|
|
11933
|
+
existing.push(trace);
|
|
11934
|
+
} else {
|
|
11935
|
+
this.tracesByPinPairKey.set(key, [trace]);
|
|
11936
|
+
}
|
|
11937
|
+
}
|
|
11938
|
+
}
|
|
11939
|
+
getConstructorParams() {
|
|
11940
|
+
return [
|
|
11941
|
+
{
|
|
11942
|
+
inputProblem: this.inputProblem,
|
|
11943
|
+
traces: this.traces,
|
|
11944
|
+
netLabelPlacements: this.inputNetLabelPlacements
|
|
11945
|
+
}
|
|
11946
|
+
];
|
|
11947
|
+
}
|
|
11948
|
+
_step() {
|
|
11949
|
+
const directConnection = this.queuedDirectConnections.shift();
|
|
11950
|
+
if (!directConnection) {
|
|
11951
|
+
this.solved = true;
|
|
11952
|
+
this.stats.inlineNetLabelCount = this.inlineNetLabelPlacements.length;
|
|
11953
|
+
return;
|
|
11954
|
+
}
|
|
11955
|
+
const placement = this.computeInlinePlacement(directConnection);
|
|
11956
|
+
if (placement) {
|
|
11957
|
+
this.inlineNetLabelPlacements.push(placement);
|
|
11958
|
+
}
|
|
11959
|
+
}
|
|
11960
|
+
computeInlinePlacement(directConnection) {
|
|
11961
|
+
const traces = this.tracesByPinPairKey.get(getPinPairKey2(directConnection.pinIds)) ?? [];
|
|
11962
|
+
if (traces.length === 0) return null;
|
|
11963
|
+
const trace = traces[0];
|
|
11964
|
+
const segments = getAxisAlignedSegments(trace.tracePath);
|
|
11965
|
+
if (segments.length === 0) return null;
|
|
11966
|
+
const height = directConnection.inlineNetLabelHeight ?? DEFAULT_INLINE_NET_LABEL_HEIGHT;
|
|
11967
|
+
const width = directConnection.inlineNetLabelWidth ?? directConnection.netLabelWidth ?? estimateInlineNetLabelWidth(directConnection.netId, height);
|
|
11968
|
+
const offset = height / 2 + INLINE_NET_LABEL_TRACE_MARGIN;
|
|
11969
|
+
const usableSegments = segments.filter(
|
|
11970
|
+
(segment) => segment.length >= width * MIN_INLINE_NET_LABEL_SEGMENT_RATIO
|
|
11971
|
+
).sort((a, b) => {
|
|
11972
|
+
const aFits = a.length >= width;
|
|
11973
|
+
const bFits = b.length >= width;
|
|
11974
|
+
if (aFits !== bFits) return aFits ? -1 : 1;
|
|
11975
|
+
return b.length - a.length;
|
|
11976
|
+
});
|
|
11977
|
+
for (const segment of usableSegments) {
|
|
11978
|
+
const sides = segment.axis === "x" ? ["y+", "y-"] : ["x-", "x+"];
|
|
11979
|
+
for (const side of sides) {
|
|
11980
|
+
for (const anchorPoint of getAnchorCandidates(segment, width)) {
|
|
11981
|
+
const center = side === "y+" ? { x: anchorPoint.x, y: anchorPoint.y + offset } : side === "y-" ? { x: anchorPoint.x, y: anchorPoint.y - offset } : side === "x-" ? { x: anchorPoint.x - offset, y: anchorPoint.y } : { x: anchorPoint.x + offset, y: anchorPoint.y };
|
|
11982
|
+
const halfAlong = width / 2;
|
|
11983
|
+
const halfAcross = height / 2;
|
|
11984
|
+
const bounds = segment.axis === "x" ? {
|
|
11985
|
+
minX: center.x - halfAlong,
|
|
11986
|
+
maxX: center.x + halfAlong,
|
|
11987
|
+
minY: center.y - halfAcross,
|
|
11988
|
+
maxY: center.y + halfAcross
|
|
11989
|
+
} : {
|
|
11990
|
+
minX: center.x - halfAcross,
|
|
11991
|
+
maxX: center.x + halfAcross,
|
|
11992
|
+
minY: center.y - halfAlong,
|
|
11993
|
+
maxY: center.y + halfAlong
|
|
11994
|
+
};
|
|
11995
|
+
if (this.isObstructed(bounds, trace)) continue;
|
|
11996
|
+
return {
|
|
11997
|
+
globalConnNetId: trace.globalConnNetId,
|
|
11998
|
+
netId: directConnection.netId,
|
|
11999
|
+
mspPairId: trace.mspPairId,
|
|
12000
|
+
pinIds: [...directConnection.pinIds],
|
|
12001
|
+
axis: segment.axis,
|
|
12002
|
+
anchorPoint,
|
|
12003
|
+
center,
|
|
12004
|
+
width,
|
|
12005
|
+
height,
|
|
12006
|
+
side
|
|
12007
|
+
};
|
|
12008
|
+
}
|
|
12009
|
+
}
|
|
12010
|
+
}
|
|
12011
|
+
return null;
|
|
12012
|
+
}
|
|
12013
|
+
/**
|
|
12014
|
+
* An inline label may not sit on top of a chip, a component's text, or a
|
|
12015
|
+
* trace belonging to another net.
|
|
12016
|
+
*/
|
|
12017
|
+
isObstructed(bounds, ownTrace) {
|
|
12018
|
+
for (const chip of this.inputProblem.chips) {
|
|
12019
|
+
const chipBounds = {
|
|
12020
|
+
minX: chip.center.x - chip.width / 2,
|
|
12021
|
+
maxX: chip.center.x + chip.width / 2,
|
|
12022
|
+
minY: chip.center.y - chip.height / 2,
|
|
12023
|
+
maxY: chip.center.y + chip.height / 2
|
|
12024
|
+
};
|
|
12025
|
+
if (boundsOverlap(bounds, chipBounds)) return true;
|
|
12026
|
+
}
|
|
12027
|
+
for (const textBox of this.inputProblem.textBoxes ?? []) {
|
|
12028
|
+
if (boundsOverlap(bounds, getTextBoxBounds(textBox))) return true;
|
|
12029
|
+
}
|
|
12030
|
+
for (const trace of this.traces) {
|
|
12031
|
+
if (trace.mspPairId === ownTrace.mspPairId) continue;
|
|
12032
|
+
if (trace.globalConnNetId === ownTrace.globalConnNetId) continue;
|
|
12033
|
+
if (doesPathIntersectBounds(trace.tracePath, bounds)) return true;
|
|
12034
|
+
}
|
|
12035
|
+
return false;
|
|
12036
|
+
}
|
|
12037
|
+
/**
|
|
12038
|
+
* Net label placements superseded by an inline label. A net gets one label or
|
|
12039
|
+
* the other, never both.
|
|
12040
|
+
*/
|
|
12041
|
+
getSupersededNetLabelKeys() {
|
|
12042
|
+
const keys = /* @__PURE__ */ new Set();
|
|
12043
|
+
for (const placement of this.inlineNetLabelPlacements) {
|
|
12044
|
+
keys.add(placement.globalConnNetId);
|
|
12045
|
+
}
|
|
12046
|
+
return keys;
|
|
12047
|
+
}
|
|
12048
|
+
getOutput() {
|
|
12049
|
+
const superseded = this.getSupersededNetLabelKeys();
|
|
12050
|
+
return {
|
|
12051
|
+
traces: this.traces,
|
|
12052
|
+
netLabelPlacements: this.inputNetLabelPlacements.filter(
|
|
12053
|
+
(placement) => !superseded.has(placement.globalConnNetId)
|
|
12054
|
+
),
|
|
12055
|
+
inlineNetLabelPlacements: this.inlineNetLabelPlacements
|
|
12056
|
+
};
|
|
12057
|
+
}
|
|
12058
|
+
visualize() {
|
|
12059
|
+
const graphics = visualizeInputProblem(this.inputProblem);
|
|
12060
|
+
graphics.lines ??= [];
|
|
12061
|
+
graphics.rects ??= [];
|
|
12062
|
+
graphics.points ??= [];
|
|
12063
|
+
graphics.texts ??= [];
|
|
12064
|
+
for (const trace of this.traces) {
|
|
12065
|
+
graphics.lines.push({
|
|
12066
|
+
points: trace.tracePath,
|
|
12067
|
+
strokeColor: "purple"
|
|
12068
|
+
});
|
|
12069
|
+
}
|
|
12070
|
+
const { netLabelPlacements } = this.getOutput();
|
|
12071
|
+
for (const label of netLabelPlacements) {
|
|
12072
|
+
graphics.rects.push({
|
|
12073
|
+
center: label.center,
|
|
12074
|
+
width: label.width,
|
|
12075
|
+
height: label.height,
|
|
12076
|
+
fill: getColorFromString(label.globalConnNetId, 0.35),
|
|
12077
|
+
strokeColor: getColorFromString(label.globalConnNetId, 0.9),
|
|
12078
|
+
label: `netId: ${label.netId}
|
|
12079
|
+
globalConnNetId: ${label.globalConnNetId}`
|
|
12080
|
+
});
|
|
12081
|
+
graphics.points.push({
|
|
12082
|
+
x: label.anchorPoint.x,
|
|
12083
|
+
y: label.anchorPoint.y,
|
|
12084
|
+
color: getColorFromString(label.globalConnNetId, 0.9),
|
|
12085
|
+
label: `anchorPoint
|
|
12086
|
+
orientation: ${label.orientation}`
|
|
12087
|
+
});
|
|
12088
|
+
}
|
|
12089
|
+
for (const inlineLabel of this.inlineNetLabelPlacements) {
|
|
12090
|
+
const isHorizontal4 = inlineLabel.axis === "x";
|
|
12091
|
+
graphics.rects.push({
|
|
12092
|
+
center: inlineLabel.center,
|
|
12093
|
+
width: isHorizontal4 ? inlineLabel.width : inlineLabel.height,
|
|
12094
|
+
height: isHorizontal4 ? inlineLabel.height : inlineLabel.width,
|
|
12095
|
+
fill: getColorFromString(inlineLabel.globalConnNetId, 0.35),
|
|
12096
|
+
strokeColor: "green",
|
|
12097
|
+
label: [
|
|
12098
|
+
`INLINE netId: ${inlineLabel.netId}`,
|
|
12099
|
+
`axis: ${inlineLabel.axis}`,
|
|
12100
|
+
`side: ${inlineLabel.side}`
|
|
12101
|
+
].join("\n")
|
|
12102
|
+
});
|
|
12103
|
+
graphics.texts.push({
|
|
12104
|
+
x: inlineLabel.center.x,
|
|
12105
|
+
y: inlineLabel.center.y,
|
|
12106
|
+
text: inlineLabel.netId ?? "",
|
|
12107
|
+
color: "green",
|
|
12108
|
+
fontSize: inlineLabel.height,
|
|
12109
|
+
anchorSide: "center"
|
|
12110
|
+
});
|
|
12111
|
+
graphics.points.push({
|
|
12112
|
+
x: inlineLabel.anchorPoint.x,
|
|
12113
|
+
y: inlineLabel.anchorPoint.y,
|
|
12114
|
+
color: "green",
|
|
12115
|
+
label: `inline anchor
|
|
12116
|
+
${inlineLabel.netId}`
|
|
12117
|
+
});
|
|
12118
|
+
}
|
|
12119
|
+
return graphics;
|
|
12120
|
+
}
|
|
12121
|
+
};
|
|
12122
|
+
var estimateInlineNetLabelWidth = (text, fontSize = DEFAULT_INLINE_NET_LABEL_HEIGHT) => {
|
|
12123
|
+
const fontScale = fontSize / 0.18;
|
|
12124
|
+
return text.length * 0.12 * fontScale + 0.12 * fontScale;
|
|
12125
|
+
};
|
|
12126
|
+
var getAnchorCandidates = (segment, labelWidth, step = 0.1) => {
|
|
12127
|
+
const along = segment.axis === "x" ? "x" : "y";
|
|
12128
|
+
const start = segment.start[along];
|
|
12129
|
+
const end = segment.end[along];
|
|
12130
|
+
const mid = (start + end) / 2;
|
|
12131
|
+
const direction = Math.sign(end - start) || 1;
|
|
12132
|
+
const pointAt = (value) => segment.axis === "x" ? { x: value, y: segment.start.y } : { x: segment.start.x, y: segment.start.y + (value - start) };
|
|
12133
|
+
const slack = segment.length - labelWidth;
|
|
12134
|
+
if (slack <= 0) return [pointAt(mid)];
|
|
12135
|
+
const offsets = [0];
|
|
12136
|
+
for (let offset = step; offset <= slack / 2 + 1e-9; offset += step) {
|
|
12137
|
+
offsets.push(offset, -offset);
|
|
12138
|
+
}
|
|
12139
|
+
offsets.push(slack / 2, -slack / 2);
|
|
12140
|
+
return offsets.map((offset) => pointAt(mid + offset * direction));
|
|
12141
|
+
};
|
|
12142
|
+
var doesPathIntersectBounds = (path, bounds) => {
|
|
12143
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
12144
|
+
const a = path[i];
|
|
12145
|
+
const b = path[i + 1];
|
|
12146
|
+
const segmentBounds = {
|
|
12147
|
+
minX: Math.min(a.x, b.x),
|
|
12148
|
+
maxX: Math.max(a.x, b.x),
|
|
12149
|
+
minY: Math.min(a.y, b.y),
|
|
12150
|
+
maxY: Math.max(a.y, b.y)
|
|
12151
|
+
};
|
|
12152
|
+
if (boundsOverlap(segmentBounds, bounds)) return true;
|
|
12153
|
+
}
|
|
12154
|
+
return false;
|
|
12155
|
+
};
|
|
12156
|
+
|
|
11528
12157
|
// lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts
|
|
11529
12158
|
function definePipelineStep(solverName, solverClass, getConstructorParams, opts = {}) {
|
|
11530
12159
|
return {
|
|
@@ -11555,7 +12184,11 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11555
12184
|
netLabelTraceCollisionSolver;
|
|
11556
12185
|
traceCleanupSolver2;
|
|
11557
12186
|
netLabelNetLabelCollisionSolver;
|
|
12187
|
+
traceElbowTransitionSimplificationSolver;
|
|
12188
|
+
preAlignmentTraceElbowTransitionSimplificationSolver;
|
|
12189
|
+
finalTraceElbowTransitionSimplificationSolver;
|
|
11558
12190
|
sameNetJunctionAlignmentSolver;
|
|
12191
|
+
inlineNetLabelSolver;
|
|
11559
12192
|
startTimeOfPhase;
|
|
11560
12193
|
endTimeOfPhase;
|
|
11561
12194
|
timeSpentOnPhase;
|
|
@@ -11676,8 +12309,24 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11676
12309
|
];
|
|
11677
12310
|
}
|
|
11678
12311
|
),
|
|
12312
|
+
definePipelineStep(
|
|
12313
|
+
"traceElbowTransitionSimplificationSolver",
|
|
12314
|
+
TraceElbowTransitionSimplificationSolver,
|
|
12315
|
+
(instance) => {
|
|
12316
|
+
const overlapAvoidanceOutput = instance.traceLabelOverlapAvoidanceSolver.getOutput();
|
|
12317
|
+
return [
|
|
12318
|
+
{
|
|
12319
|
+
inputProblem: instance.inputProblem,
|
|
12320
|
+
traces: overlapAvoidanceOutput.traces,
|
|
12321
|
+
completedReroutes: overlapAvoidanceOutput.completedReroutes,
|
|
12322
|
+
netLabelPlacements: instance.traceLabelOverlapAvoidanceSolver.netLabelPlacements,
|
|
12323
|
+
paddingBuffer: 0.1
|
|
12324
|
+
}
|
|
12325
|
+
];
|
|
12326
|
+
}
|
|
12327
|
+
),
|
|
11679
12328
|
definePipelineStep("traceCleanupSolver", TraceCleanupSolver, (instance) => {
|
|
11680
|
-
const prevSolverOutput = instance.
|
|
12329
|
+
const prevSolverOutput = instance.traceElbowTransitionSimplificationSolver.getOutput();
|
|
11681
12330
|
const traces = prevSolverOutput.traces;
|
|
11682
12331
|
const labelMergingOutput = instance.traceLabelOverlapAvoidanceSolver.labelMergingSolver.getOutput();
|
|
11683
12332
|
return [
|
|
@@ -11779,11 +12428,27 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11779
12428
|
}
|
|
11780
12429
|
]
|
|
11781
12430
|
),
|
|
12431
|
+
definePipelineStep(
|
|
12432
|
+
"preAlignmentTraceElbowTransitionSimplificationSolver",
|
|
12433
|
+
TraceElbowTransitionSimplificationSolver,
|
|
12434
|
+
(instance) => {
|
|
12435
|
+
const collisionOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
|
|
12436
|
+
return [
|
|
12437
|
+
{
|
|
12438
|
+
inputProblem: instance.inputProblem,
|
|
12439
|
+
traces: collisionOutput.traces,
|
|
12440
|
+
completedReroutes: collisionOutput.completedReroutes,
|
|
12441
|
+
netLabelPlacements: collisionOutput.netLabelPlacements,
|
|
12442
|
+
paddingBuffer: 0.1
|
|
12443
|
+
}
|
|
12444
|
+
];
|
|
12445
|
+
}
|
|
12446
|
+
),
|
|
11782
12447
|
definePipelineStep(
|
|
11783
12448
|
"traceCleanupSolver2",
|
|
11784
12449
|
TraceCleanupSolver,
|
|
11785
12450
|
(instance) => {
|
|
11786
|
-
const collisionOutput = instance.
|
|
12451
|
+
const collisionOutput = instance.preAlignmentTraceElbowTransitionSimplificationSolver.getOutput();
|
|
11787
12452
|
const labelMergingOutput = instance.traceLabelOverlapAvoidanceSolver.labelMergingSolver.getOutput();
|
|
11788
12453
|
return [
|
|
11789
12454
|
{
|
|
@@ -11836,16 +12501,35 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11836
12501
|
];
|
|
11837
12502
|
}
|
|
11838
12503
|
),
|
|
12504
|
+
definePipelineStep(
|
|
12505
|
+
"finalTraceElbowTransitionSimplificationSolver",
|
|
12506
|
+
TraceElbowTransitionSimplificationSolver,
|
|
12507
|
+
(instance) => {
|
|
12508
|
+
const collisionOutput = instance.netLabelTraceCollisionSolver.getOutput();
|
|
12509
|
+
return [
|
|
12510
|
+
{
|
|
12511
|
+
inputProblem: instance.inputProblem,
|
|
12512
|
+
traces: collisionOutput.traces,
|
|
12513
|
+
completedReroutes: collisionOutput.completedReroutes,
|
|
12514
|
+
netLabelPlacements: collisionOutput.netLabelPlacements,
|
|
12515
|
+
paddingBuffer: 0.1
|
|
12516
|
+
}
|
|
12517
|
+
];
|
|
12518
|
+
}
|
|
12519
|
+
),
|
|
11839
12520
|
definePipelineStep(
|
|
11840
12521
|
"netLabelNetLabelCollisionSolver",
|
|
11841
12522
|
NetLabelNetLabelCollisionSolver,
|
|
11842
|
-
(instance) =>
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
12523
|
+
(instance) => {
|
|
12524
|
+
const simplificationOutput = instance.finalTraceElbowTransitionSimplificationSolver.getOutput();
|
|
12525
|
+
return [
|
|
12526
|
+
{
|
|
12527
|
+
inputProblem: instance.inputProblem,
|
|
12528
|
+
traces: simplificationOutput.traces,
|
|
12529
|
+
netLabelPlacements: simplificationOutput.netLabelPlacements
|
|
12530
|
+
}
|
|
12531
|
+
];
|
|
12532
|
+
}
|
|
11849
12533
|
),
|
|
11850
12534
|
definePipelineStep(
|
|
11851
12535
|
"sameNetJunctionAlignmentSolver",
|
|
@@ -11860,6 +12544,20 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11860
12544
|
}
|
|
11861
12545
|
];
|
|
11862
12546
|
}
|
|
12547
|
+
),
|
|
12548
|
+
definePipelineStep(
|
|
12549
|
+
"inlineNetLabelSolver",
|
|
12550
|
+
InlineNetLabelSolver,
|
|
12551
|
+
(instance) => {
|
|
12552
|
+
const junctionOutput = instance.sameNetJunctionAlignmentSolver.getOutput();
|
|
12553
|
+
return [
|
|
12554
|
+
{
|
|
12555
|
+
inputProblem: instance.inputProblem,
|
|
12556
|
+
traces: junctionOutput.traces,
|
|
12557
|
+
netLabelPlacements: junctionOutput.netLabelPlacements
|
|
12558
|
+
}
|
|
12559
|
+
];
|
|
12560
|
+
}
|
|
11863
12561
|
)
|
|
11864
12562
|
];
|
|
11865
12563
|
constructor(inputProblem, opts) {
|
|
@@ -11973,6 +12671,11 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11973
12671
|
}
|
|
11974
12672
|
};
|
|
11975
12673
|
export {
|
|
12674
|
+
DEFAULT_INLINE_NET_LABEL_HEIGHT,
|
|
12675
|
+
INLINE_NET_LABEL_TRACE_MARGIN,
|
|
12676
|
+
InlineNetLabelSolver,
|
|
12677
|
+
MIN_INLINE_NET_LABEL_SEGMENT_RATIO,
|
|
11976
12678
|
SchematicTracePipelineSolver,
|
|
11977
|
-
SchematicTraceSingleLineSolver2
|
|
12679
|
+
SchematicTraceSingleLineSolver2,
|
|
12680
|
+
estimateInlineNetLabelWidth
|
|
11978
12681
|
};
|