@tscircuit/schematic-trace-solver 0.0.120 → 0.0.122
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 +10 -3
- package/dist/index.js +350 -78
- package/lib/solvers/RailNetLabelCornerPlacementSolver/RailNetLabelCornerPlacementSolver.ts +223 -21
- package/lib/solvers/RailNetLabelCornerPlacementSolver/types.ts +3 -3
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +34 -9
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/evaluateRailGroup.ts +79 -59
- package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailAlignmentFallbackCoordinates.ts +96 -0
- package/lib/utils/doesPathCoincideWithTraces.ts +71 -1
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260804T095800Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +4 -4
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +5 -5
- package/tests/bug-reports/bug-report-20260717T042845Z/__snapshots__/bug-report-20260717T042845Z.snap.svg +12 -12
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +135 -0
- package/tests/bug-reports/bug-report-20260804T095800Z/bug-report-20260804T095800Z.json +634 -0
- package/tests/bug-reports/bug-report-20260804T095800Z/bug-report-20260804T095800Z.test.ts +12 -0
- package/tests/examples/__snapshots__/example12.snap.svg +2 -2
- package/tests/examples/__snapshots__/example13.snap.svg +4 -4
- package/tests/examples/__snapshots__/example14.snap.svg +36 -36
- package/tests/examples/__snapshots__/example31.snap.svg +6 -6
- package/tests/examples/__snapshots__/example43.snap.svg +2 -2
- package/tests/examples/__snapshots__/example44.snap.svg +2 -2
- package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +2 -2
- package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +26 -26
- package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-atmega328p-fault-pullup.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-atmega328p-missing-gnd-netlabel.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +101 -0
- package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +18 -18
- package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +3 -3
- package/tests/repros/__snapshots__/repro-nrf52810-clock-routing.snap.svg +82 -0
- package/tests/repros/assets/repro-bq25895-cross-net-trace-overlap.input.json +498 -0
- package/tests/repros/assets/repro-nrf52810-clock-routing.input.json +234 -0
- package/tests/repros/repro-bq25895-cross-net-trace-overlap.test.ts +88 -0
- package/tests/repros/repro-missing-trace-netlabel.test.ts +12 -0
- package/tests/repros/repro-netlabel-overlap-trace.test.ts +38 -0
- package/tests/repros/repro-nrf52810-clock-routing.test.ts +28 -0
- package/tests/solvers/TraceCleanupSolver/alignSameNetRails-trace-clearance.test.ts +68 -0
package/dist/index.d.ts
CHANGED
|
@@ -828,11 +828,11 @@ type TraceCornerCandidate = {
|
|
|
828
828
|
traceId: string;
|
|
829
829
|
distance: number;
|
|
830
830
|
/**
|
|
831
|
-
* True when the corner
|
|
832
|
-
*
|
|
833
|
-
* a clean stub off that pin rather than floating over a mid-trace bend.
|
|
831
|
+
* True when the corner is on a vertical rail that connects directly to one
|
|
832
|
+
* of the trace's endpoint pins, rather than on an arbitrary middle bend.
|
|
834
833
|
*/
|
|
835
834
|
pinAligned: boolean;
|
|
835
|
+
reroutedTracePath?: Point[];
|
|
836
836
|
};
|
|
837
837
|
type CornerCandidateStatus = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "netlabel-collision";
|
|
838
838
|
type EvaluatedCornerCandidate = TraceCornerCandidate & {
|
|
@@ -859,6 +859,7 @@ declare class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
859
859
|
getConstructorParams(): ConstructorParameters<typeof RailNetLabelCornerPlacementSolver>[0];
|
|
860
860
|
_step(): void;
|
|
861
861
|
getOutput(): {
|
|
862
|
+
traces: SolvedTracePath[];
|
|
862
863
|
netLabelPlacements: NetLabelPlacement[];
|
|
863
864
|
};
|
|
864
865
|
private getProcessableLabelIndices;
|
|
@@ -873,6 +874,12 @@ declare class RailNetLabelCornerPlacementSolver extends BaseSolver {
|
|
|
873
874
|
private intersectsAnyChip;
|
|
874
875
|
private intersectsAnyOtherNetLabel;
|
|
875
876
|
private getCornerCandidatesForLabel;
|
|
877
|
+
private isConfiguredRailLabel;
|
|
878
|
+
private pointsEqual;
|
|
879
|
+
private getVerticalSegmentPointIndices;
|
|
880
|
+
private isPinAlignedCorner;
|
|
881
|
+
private getClearanceShiftedCornerCandidates;
|
|
882
|
+
private isReroutedTraceClear;
|
|
876
883
|
private getTraceLinesForLabel;
|
|
877
884
|
private isTraceForLabel;
|
|
878
885
|
visualize(): GraphicsObject;
|
package/dist/index.js
CHANGED
|
@@ -3922,7 +3922,12 @@ var generateRerouteCandidates = ({
|
|
|
3922
3922
|
};
|
|
3923
3923
|
|
|
3924
3924
|
// lib/utils/doesPathCoincideWithTraces.ts
|
|
3925
|
+
import { boundsIntersection } from "@tscircuit/math-utils";
|
|
3925
3926
|
var COINCIDENT_EPS = 2e-3;
|
|
3927
|
+
var GEOMETRY_EPS = 1e-6;
|
|
3928
|
+
var SCHEMATIC_TRACE_STROKE_WIDTH = 0.02;
|
|
3929
|
+
var SCHEMATIC_TRACE_MIN_CENTERLINE_CLEARANCE = SCHEMATIC_TRACE_STROKE_WIDTH + GEOMETRY_EPS;
|
|
3930
|
+
var SCHEMATIC_TRACE_MIN_VISUAL_CENTERLINE_CLEARANCE = SCHEMATIC_TRACE_STROKE_WIDTH * 3;
|
|
3926
3931
|
var doesPathCoincideWithTraces = (path, traces) => {
|
|
3927
3932
|
const rangesOverlap1D = (a1, a2, b1, b2) => Math.min(Math.max(a1, a2), Math.max(b1, b2)) - Math.max(Math.min(a1, a2), Math.min(b1, b2)) > COINCIDENT_EPS;
|
|
3928
3933
|
for (let i = 0; i < path.length - 1; i++) {
|
|
@@ -3954,6 +3959,44 @@ var doesPathCoincideWithTraces = (path, traces) => {
|
|
|
3954
3959
|
}
|
|
3955
3960
|
return false;
|
|
3956
3961
|
};
|
|
3962
|
+
var doesPathOverlapTraceStrokes = (path, traces) => {
|
|
3963
|
+
const traceStrokeRadius = SCHEMATIC_TRACE_MIN_CENTERLINE_CLEARANCE / 2;
|
|
3964
|
+
const getStrokeBounds = (start, end, isVertical5) => isVertical5 ? {
|
|
3965
|
+
minX: start.x - traceStrokeRadius,
|
|
3966
|
+
maxX: start.x + traceStrokeRadius,
|
|
3967
|
+
minY: Math.min(start.y, end.y),
|
|
3968
|
+
maxY: Math.max(start.y, end.y)
|
|
3969
|
+
} : {
|
|
3970
|
+
minX: Math.min(start.x, end.x),
|
|
3971
|
+
maxX: Math.max(start.x, end.x),
|
|
3972
|
+
minY: start.y - traceStrokeRadius,
|
|
3973
|
+
maxY: start.y + traceStrokeRadius
|
|
3974
|
+
};
|
|
3975
|
+
for (let pathIndex = 0; pathIndex < path.length - 1; pathIndex++) {
|
|
3976
|
+
const pathStart = path[pathIndex];
|
|
3977
|
+
const pathEnd = path[pathIndex + 1];
|
|
3978
|
+
const isVertical5 = Math.abs(pathStart.x - pathEnd.x) < COINCIDENT_EPS;
|
|
3979
|
+
const isHorizontal4 = Math.abs(pathStart.y - pathEnd.y) < COINCIDENT_EPS;
|
|
3980
|
+
if (!isVertical5 && !isHorizontal4) continue;
|
|
3981
|
+
const pathBounds = getStrokeBounds(pathStart, pathEnd, isVertical5);
|
|
3982
|
+
for (const trace of traces) {
|
|
3983
|
+
for (let traceIndex = 0; traceIndex < trace.tracePath.length - 1; traceIndex++) {
|
|
3984
|
+
const traceStart = trace.tracePath[traceIndex];
|
|
3985
|
+
const traceEnd = trace.tracePath[traceIndex + 1];
|
|
3986
|
+
const isParallel = isVertical5 ? Math.abs(traceStart.x - traceEnd.x) < COINCIDENT_EPS : Math.abs(traceStart.y - traceEnd.y) < COINCIDENT_EPS;
|
|
3987
|
+
if (!isParallel) continue;
|
|
3988
|
+
const overlap = boundsIntersection(
|
|
3989
|
+
pathBounds,
|
|
3990
|
+
getStrokeBounds(traceStart, traceEnd, isVertical5)
|
|
3991
|
+
);
|
|
3992
|
+
if (!overlap) continue;
|
|
3993
|
+
const overlapLength = isVertical5 ? overlap.maxY - overlap.minY : overlap.maxX - overlap.minX;
|
|
3994
|
+
if (overlapLength > COINCIDENT_EPS) return true;
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
return false;
|
|
3999
|
+
};
|
|
3957
4000
|
|
|
3958
4001
|
// lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts
|
|
3959
4002
|
var MAX_TRIES = 5;
|
|
@@ -5307,6 +5350,53 @@ var balanceZShapes = ({
|
|
|
5307
5350
|
};
|
|
5308
5351
|
};
|
|
5309
5352
|
|
|
5353
|
+
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getRailAlignmentFallbackCoordinates.ts
|
|
5354
|
+
import { boundsIntersection as boundsIntersection2 } from "@tscircuit/math-utils";
|
|
5355
|
+
var getAlongBounds = (orientation, minAlong, maxAlong) => orientation === "vertical" ? { minX: 0, maxX: 0, minY: minAlong, maxY: maxAlong } : { minX: minAlong, maxX: maxAlong, minY: 0, maxY: 0 };
|
|
5356
|
+
var hasPositiveAlongOverlap = (orientation, first, second) => {
|
|
5357
|
+
const overlap = boundsIntersection2(first, second);
|
|
5358
|
+
if (!overlap) return false;
|
|
5359
|
+
const overlapLength = orientation === "vertical" ? overlap.maxY - overlap.minY : overlap.maxX - overlap.minX;
|
|
5360
|
+
return overlapLength > RAIL_ALIGNMENT_EPSILON;
|
|
5361
|
+
};
|
|
5362
|
+
var getRailAlignmentFallbackCoordinates = ({
|
|
5363
|
+
group,
|
|
5364
|
+
originalCoordinates,
|
|
5365
|
+
otherNetTraces
|
|
5366
|
+
}) => {
|
|
5367
|
+
const orientation = group[0].orientation;
|
|
5368
|
+
const groupAlongBounds = group.map(
|
|
5369
|
+
(segment) => getAlongBounds(orientation, segment.minAlong, segment.maxAlong)
|
|
5370
|
+
);
|
|
5371
|
+
const fallbackClearance = SCHEMATIC_TRACE_MIN_VISUAL_CENTERLINE_CLEARANCE;
|
|
5372
|
+
const coordinates = [];
|
|
5373
|
+
for (const trace of otherNetTraces) {
|
|
5374
|
+
for (let index = 0; index < trace.tracePath.length - 1; index++) {
|
|
5375
|
+
const start = trace.tracePath[index];
|
|
5376
|
+
const end = trace.tracePath[index + 1];
|
|
5377
|
+
if (getRailOrientation(start, end) !== orientation) continue;
|
|
5378
|
+
const minAlong = orientation === "vertical" ? Math.min(start.y, end.y) : Math.min(start.x, end.x);
|
|
5379
|
+
const maxAlong = orientation === "vertical" ? Math.max(start.y, end.y) : Math.max(start.x, end.x);
|
|
5380
|
+
const traceAlongBounds = getAlongBounds(orientation, minAlong, maxAlong);
|
|
5381
|
+
if (!groupAlongBounds.some(
|
|
5382
|
+
(bounds) => hasPositiveAlongOverlap(orientation, bounds, traceAlongBounds)
|
|
5383
|
+
)) {
|
|
5384
|
+
continue;
|
|
5385
|
+
}
|
|
5386
|
+
const coordinate = orientation === "vertical" ? start.x : start.y;
|
|
5387
|
+
const touchesOriginalCandidate = originalCoordinates.some(
|
|
5388
|
+
(originalCoordinate) => Math.abs(originalCoordinate - coordinate) <= SCHEMATIC_TRACE_MIN_CENTERLINE_CLEARANCE
|
|
5389
|
+
);
|
|
5390
|
+
if (!touchesOriginalCandidate) continue;
|
|
5391
|
+
coordinates.push(
|
|
5392
|
+
coordinate - fallbackClearance,
|
|
5393
|
+
coordinate + fallbackClearance
|
|
5394
|
+
);
|
|
5395
|
+
}
|
|
5396
|
+
}
|
|
5397
|
+
return getDistinctCoordinates(coordinates);
|
|
5398
|
+
};
|
|
5399
|
+
|
|
5310
5400
|
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/moveRailSegments.ts
|
|
5311
5401
|
var moveRailSegments = (trace, segments, coordinate) => {
|
|
5312
5402
|
const pointsToMove = /* @__PURE__ */ new Set();
|
|
@@ -5578,7 +5668,7 @@ var evaluateRailGroup = ({
|
|
|
5578
5668
|
(trace) => groupTraceIds.has(trace.mspPairId)
|
|
5579
5669
|
);
|
|
5580
5670
|
const baseline = getTraceGeometryMetrics(originalGroupTraces, traces);
|
|
5581
|
-
const
|
|
5671
|
+
const originalCoordinates = getDistinctCoordinates(
|
|
5582
5672
|
group.map((segment) => segment.coordinate)
|
|
5583
5673
|
);
|
|
5584
5674
|
const otherNetTraces = traces.filter(
|
|
@@ -5587,58 +5677,72 @@ var evaluateRailGroup = ({
|
|
|
5587
5677
|
const immutableSameNetTraces = traces.filter(
|
|
5588
5678
|
(trace) => trace.globalConnNetId === group[0].globalConnNetId && !eligibleTraceIds.has(trace.mspPairId)
|
|
5589
5679
|
);
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
const
|
|
5593
|
-
|
|
5594
|
-
const
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5680
|
+
const evaluateCoordinates = (coordinates) => {
|
|
5681
|
+
let best = null;
|
|
5682
|
+
for (const coordinate of coordinates) {
|
|
5683
|
+
const candidateMap = /* @__PURE__ */ new Map();
|
|
5684
|
+
for (const trace of originalGroupTraces) {
|
|
5685
|
+
const candidateTrace = moveRailSegments(
|
|
5686
|
+
trace,
|
|
5687
|
+
group.filter((segment) => segment.traceId === trace.mspPairId),
|
|
5688
|
+
coordinate
|
|
5689
|
+
);
|
|
5690
|
+
candidateMap.set(trace.mspPairId, candidateTrace);
|
|
5691
|
+
}
|
|
5692
|
+
const candidateTraces = [...candidateMap.values()];
|
|
5693
|
+
const allCandidateTraces = traces.map(
|
|
5694
|
+
(trace) => candidateMap.get(trace.mspPairId) ?? trace
|
|
5598
5695
|
);
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
netLabels: netLabelPlacements
|
|
5609
|
-
}).length === 0 && !doesPathCoincideWithTraces(candidate2.tracePath, otherNetTraces) && !doesPathCoincideWithTraces(
|
|
5610
|
-
candidate2.tracePath,
|
|
5611
|
-
immutableSameNetTraces.filter(
|
|
5612
|
-
(trace) => trace.mspPairId !== candidate2.mspPairId
|
|
5696
|
+
const candidatesAreClear = candidateTraces.every(
|
|
5697
|
+
(candidate2) => !isPathCollidingWithObstacles(candidate2.tracePath, obstacles) && detectTraceLabelOverlap({
|
|
5698
|
+
traces: [candidate2],
|
|
5699
|
+
netLabels: netLabelPlacements
|
|
5700
|
+
}).length === 0 && !doesPathOverlapTraceStrokes(candidate2.tracePath, otherNetTraces) && !doesPathCoincideWithTraces(
|
|
5701
|
+
candidate2.tracePath,
|
|
5702
|
+
immutableSameNetTraces.filter(
|
|
5703
|
+
(trace) => trace.mspPairId !== candidate2.mspPairId
|
|
5704
|
+
)
|
|
5613
5705
|
)
|
|
5614
|
-
)
|
|
5615
|
-
);
|
|
5616
|
-
if (!candidatesAreClear) continue;
|
|
5617
|
-
if (!preservesLabelAnchors(netLabelPlacements, traces, allCandidateTraces)) {
|
|
5618
|
-
continue;
|
|
5619
|
-
}
|
|
5620
|
-
const metrics = getTraceGeometryMetrics(candidateTraces, allCandidateTraces);
|
|
5621
|
-
if (metrics.otherNetCrossings > baseline.otherNetCrossings) continue;
|
|
5622
|
-
if (!isReadabilityImprovement(metrics, baseline)) continue;
|
|
5623
|
-
const score = {
|
|
5624
|
-
...metrics,
|
|
5625
|
-
displacement: group.reduce(
|
|
5626
|
-
(sum, segment) => sum + Math.abs(segment.coordinate - coordinate),
|
|
5627
|
-
0
|
|
5628
|
-
),
|
|
5629
|
-
coordinate
|
|
5630
|
-
};
|
|
5631
|
-
const changedTraceIds = candidateTraces.filter((candidate2) => {
|
|
5632
|
-
const original = traces.find(
|
|
5633
|
-
(trace) => trace.mspPairId === candidate2.mspPairId
|
|
5634
5706
|
);
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5707
|
+
if (!candidatesAreClear) continue;
|
|
5708
|
+
if (!preservesLabelAnchors(netLabelPlacements, traces, allCandidateTraces)) {
|
|
5709
|
+
continue;
|
|
5710
|
+
}
|
|
5711
|
+
const metrics = getTraceGeometryMetrics(
|
|
5712
|
+
candidateTraces,
|
|
5713
|
+
allCandidateTraces
|
|
5714
|
+
);
|
|
5715
|
+
if (metrics.otherNetCrossings > baseline.otherNetCrossings) continue;
|
|
5716
|
+
if (!isReadabilityImprovement(metrics, baseline)) continue;
|
|
5717
|
+
const score = {
|
|
5718
|
+
...metrics,
|
|
5719
|
+
displacement: group.reduce(
|
|
5720
|
+
(sum, segment) => sum + Math.abs(segment.coordinate - coordinate),
|
|
5721
|
+
0
|
|
5722
|
+
),
|
|
5723
|
+
coordinate
|
|
5724
|
+
};
|
|
5725
|
+
const changedTraceIds = candidateTraces.filter((candidate2) => {
|
|
5726
|
+
const original = traces.find(
|
|
5727
|
+
(trace) => trace.mspPairId === candidate2.mspPairId
|
|
5728
|
+
);
|
|
5729
|
+
return tracePathChanged(original, candidate2);
|
|
5730
|
+
}).map((trace) => trace.mspPairId);
|
|
5731
|
+
if (changedTraceIds.length === 0) continue;
|
|
5732
|
+
const candidate = { traces: allCandidateTraces, changedTraceIds, score };
|
|
5733
|
+
if (!best || scoreIsBetter(candidate.score, best.score)) best = candidate;
|
|
5734
|
+
}
|
|
5735
|
+
return best;
|
|
5736
|
+
};
|
|
5737
|
+
const originalCandidate = evaluateCoordinates(originalCoordinates);
|
|
5738
|
+
if (originalCandidate) return originalCandidate;
|
|
5739
|
+
return evaluateCoordinates(
|
|
5740
|
+
getRailAlignmentFallbackCoordinates({
|
|
5741
|
+
group,
|
|
5742
|
+
originalCoordinates,
|
|
5743
|
+
otherNetTraces
|
|
5744
|
+
})
|
|
5745
|
+
);
|
|
5642
5746
|
};
|
|
5643
5747
|
|
|
5644
5748
|
// lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getComponentSideRailSegments.ts
|
|
@@ -8948,6 +9052,7 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
8948
9052
|
}
|
|
8949
9053
|
getOutput() {
|
|
8950
9054
|
return {
|
|
9055
|
+
traces: this.traces,
|
|
8951
9056
|
netLabelPlacements: this.outputNetLabelPlacements
|
|
8952
9057
|
};
|
|
8953
9058
|
}
|
|
@@ -9000,15 +9105,25 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9000
9105
|
center,
|
|
9001
9106
|
width: label.width,
|
|
9002
9107
|
height: label.height,
|
|
9003
|
-
status: this.getCandidateStatus(bounds, labelIndex),
|
|
9108
|
+
status: this.getCandidateStatus(bounds, labelIndex, candidate),
|
|
9004
9109
|
selected: false
|
|
9005
9110
|
};
|
|
9006
9111
|
}
|
|
9007
|
-
getCandidateStatus(bounds, labelIndex) {
|
|
9112
|
+
getCandidateStatus(bounds, labelIndex, candidate) {
|
|
9008
9113
|
if (this.intersectsAnyChip(bounds)) return "chip-collision";
|
|
9009
9114
|
if (rectIntersectsAnyTextBox(bounds, this.inputProblem))
|
|
9010
9115
|
return "text-collision";
|
|
9011
|
-
|
|
9116
|
+
const candidateTraceMap = candidate.reroutedTracePath ? {
|
|
9117
|
+
...this.traceMap,
|
|
9118
|
+
[candidate.traceId]: {
|
|
9119
|
+
...this.traceMap[candidate.traceId],
|
|
9120
|
+
tracePath: candidate.reroutedTracePath
|
|
9121
|
+
}
|
|
9122
|
+
} : this.traceMap;
|
|
9123
|
+
if (traceCrossesBoundsInterior(bounds, candidateTraceMap)) {
|
|
9124
|
+
return "trace-collision";
|
|
9125
|
+
}
|
|
9126
|
+
if (candidate.reroutedTracePath && !this.isReroutedTraceClear(candidate, labelIndex)) {
|
|
9012
9127
|
return "trace-collision";
|
|
9013
9128
|
}
|
|
9014
9129
|
if (this.intersectsAnyOtherNetLabel(bounds, labelIndex)) {
|
|
@@ -9017,6 +9132,23 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9017
9132
|
return "valid";
|
|
9018
9133
|
}
|
|
9019
9134
|
applyCandidate(labelIndex, label, candidate) {
|
|
9135
|
+
if (candidate.reroutedTracePath) {
|
|
9136
|
+
const originalTrace = this.traceMap[candidate.traceId];
|
|
9137
|
+
const reroutedTrace = {
|
|
9138
|
+
...originalTrace,
|
|
9139
|
+
tracePath: candidate.reroutedTracePath
|
|
9140
|
+
};
|
|
9141
|
+
this.traces = this.traces.map(
|
|
9142
|
+
(trace) => trace.mspPairId === candidate.traceId ? reroutedTrace : trace
|
|
9143
|
+
);
|
|
9144
|
+
this.traceMap[candidate.traceId] = reroutedTrace;
|
|
9145
|
+
this.outputNetLabelPlacements = moveAttachedLabelsToReroutedTrace({
|
|
9146
|
+
trace: originalTrace,
|
|
9147
|
+
originalTracePath: originalTrace.tracePath,
|
|
9148
|
+
reroutedTracePath: candidate.reroutedTracePath,
|
|
9149
|
+
netLabelPlacements: this.outputNetLabelPlacements
|
|
9150
|
+
});
|
|
9151
|
+
}
|
|
9020
9152
|
this.outputNetLabelPlacements[labelIndex] = {
|
|
9021
9153
|
...label,
|
|
9022
9154
|
anchorPoint: candidate.anchorPoint,
|
|
@@ -9042,30 +9174,152 @@ var RailNetLabelCornerPlacementSolver = class extends BaseSolver {
|
|
|
9042
9174
|
});
|
|
9043
9175
|
}
|
|
9044
9176
|
getCornerCandidatesForLabel(label) {
|
|
9045
|
-
const
|
|
9046
|
-
const
|
|
9047
|
-
const
|
|
9048
|
-
|
|
9177
|
+
const anchorAlignedCandidates = [];
|
|
9178
|
+
const railAlignedCandidates = [];
|
|
9179
|
+
const labelTraces = this.getTraceLinesForLabel(label);
|
|
9180
|
+
const allowRailAlignedFallback = this.isConfiguredRailLabel(label) && !labelTraces.some(
|
|
9181
|
+
(trace) => getTraceCorners(trace.tracePath).some(
|
|
9182
|
+
(corner) => this.pointsEqual(corner, label.anchorPoint)
|
|
9183
|
+
)
|
|
9184
|
+
);
|
|
9185
|
+
for (const trace of labelTraces) {
|
|
9049
9186
|
const path = trace.tracePath;
|
|
9050
9187
|
const pins = [path[0], path[path.length - 1]];
|
|
9051
9188
|
for (const anchorPoint of getTraceCorners(path)) {
|
|
9052
|
-
const
|
|
9053
|
-
|
|
9054
|
-
|
|
9055
|
-
|
|
9056
|
-
|
|
9057
|
-
return Math.abs(pin.y - anchorPoint.y) < EPS6;
|
|
9058
|
-
});
|
|
9189
|
+
const cornerIndex = path.indexOf(anchorPoint);
|
|
9190
|
+
const anchorAlignedToPin = pins.some(
|
|
9191
|
+
(pin) => Math.abs(pin.x - anchorPoint.x) <= EPS6
|
|
9192
|
+
);
|
|
9193
|
+
const pinAligned = anchorAlignedToPin || allowRailAlignedFallback && this.isPinAlignedCorner(path, cornerIndex);
|
|
9059
9194
|
if (!pinAligned) continue;
|
|
9060
|
-
candidates
|
|
9195
|
+
const candidates = anchorAlignedToPin ? anchorAlignedCandidates : railAlignedCandidates;
|
|
9196
|
+
candidates.push(
|
|
9197
|
+
{
|
|
9198
|
+
anchorPoint,
|
|
9199
|
+
traceId: trace.mspPairId,
|
|
9200
|
+
distance: getDistance(anchorPoint, label.anchorPoint),
|
|
9201
|
+
pinAligned
|
|
9202
|
+
},
|
|
9203
|
+
...this.getClearanceShiftedCornerCandidates({
|
|
9204
|
+
label,
|
|
9205
|
+
trace,
|
|
9206
|
+
cornerIndex,
|
|
9207
|
+
pinAligned
|
|
9208
|
+
})
|
|
9209
|
+
);
|
|
9210
|
+
}
|
|
9211
|
+
}
|
|
9212
|
+
return [
|
|
9213
|
+
...anchorAlignedCandidates.sort((a, b) => a.distance - b.distance),
|
|
9214
|
+
...railAlignedCandidates.sort((a, b) => a.distance - b.distance)
|
|
9215
|
+
];
|
|
9216
|
+
}
|
|
9217
|
+
isConfiguredRailLabel(label) {
|
|
9218
|
+
if (!label.netId) return false;
|
|
9219
|
+
return this.inputProblem.availableNetLabelOrientations[label.netId]?.some(
|
|
9220
|
+
(orientation) => orientation === "y+" || orientation === "y-"
|
|
9221
|
+
);
|
|
9222
|
+
}
|
|
9223
|
+
pointsEqual(a, b) {
|
|
9224
|
+
return Math.abs(a.x - b.x) <= EPS6 && Math.abs(a.y - b.y) <= EPS6;
|
|
9225
|
+
}
|
|
9226
|
+
getVerticalSegmentPointIndices(path, cornerIndex) {
|
|
9227
|
+
const corner = path[cornerIndex];
|
|
9228
|
+
const previousIsVertical = Math.abs(path[cornerIndex - 1].x - corner.x) <= EPS6;
|
|
9229
|
+
const nextIsVertical = Math.abs(path[cornerIndex + 1].x - corner.x) <= EPS6;
|
|
9230
|
+
if (previousIsVertical === nextIsVertical) return null;
|
|
9231
|
+
const indices = previousIsVertical ? [cornerIndex - 1, cornerIndex] : [cornerIndex, cornerIndex + 1];
|
|
9232
|
+
return indices;
|
|
9233
|
+
}
|
|
9234
|
+
isPinAlignedCorner(path, cornerIndex) {
|
|
9235
|
+
const verticalPointIndices = this.getVerticalSegmentPointIndices(
|
|
9236
|
+
path,
|
|
9237
|
+
cornerIndex
|
|
9238
|
+
);
|
|
9239
|
+
if (!verticalPointIndices) return false;
|
|
9240
|
+
const [startIndex, endIndex] = verticalPointIndices;
|
|
9241
|
+
return startIndex <= 1 || endIndex >= path.length - 2;
|
|
9242
|
+
}
|
|
9243
|
+
getClearanceShiftedCornerCandidates({
|
|
9244
|
+
label,
|
|
9245
|
+
trace,
|
|
9246
|
+
cornerIndex,
|
|
9247
|
+
pinAligned
|
|
9248
|
+
}) {
|
|
9249
|
+
if (label.orientation !== "y+" && label.orientation !== "y-") return [];
|
|
9250
|
+
const path = trace.tracePath;
|
|
9251
|
+
const corner = path[cornerIndex];
|
|
9252
|
+
const verticalPointIndices = this.getVerticalSegmentPointIndices(
|
|
9253
|
+
path,
|
|
9254
|
+
cornerIndex
|
|
9255
|
+
);
|
|
9256
|
+
if (!verticalPointIndices || verticalPointIndices.includes(0) || verticalPointIndices.includes(path.length - 1)) {
|
|
9257
|
+
return [];
|
|
9258
|
+
}
|
|
9259
|
+
const center = getCenterFromAnchor(
|
|
9260
|
+
corner,
|
|
9261
|
+
label.orientation,
|
|
9262
|
+
label.width,
|
|
9263
|
+
label.height
|
|
9264
|
+
);
|
|
9265
|
+
const labelBounds = getRectBounds(center, label.width, label.height);
|
|
9266
|
+
const collidingChipBounds = this.inputProblem.chips.map((chip) => getRectBounds(chip.center, chip.width, chip.height)).filter((chipBounds) => rectsOverlap(labelBounds, chipBounds));
|
|
9267
|
+
const shiftedCoordinates = /* @__PURE__ */ new Set();
|
|
9268
|
+
for (const chipBounds of collidingChipBounds) {
|
|
9269
|
+
shiftedCoordinates.add(chipBounds.minX - label.width / 2);
|
|
9270
|
+
shiftedCoordinates.add(chipBounds.maxX + label.width / 2);
|
|
9271
|
+
}
|
|
9272
|
+
return [...shiftedCoordinates].flatMap((x) => {
|
|
9273
|
+
if (Math.abs(x - corner.x) <= EPS6) return [];
|
|
9274
|
+
const reroutedTracePath = simplifyPath(
|
|
9275
|
+
path.map(
|
|
9276
|
+
(point, pointIndex) => verticalPointIndices.includes(pointIndex) ? { ...point, x } : point
|
|
9277
|
+
)
|
|
9278
|
+
);
|
|
9279
|
+
const anchorPoint = { x, y: corner.y };
|
|
9280
|
+
const remainsCorner = getTraceCorners(reroutedTracePath).some(
|
|
9281
|
+
(point) => this.pointsEqual(point, anchorPoint)
|
|
9282
|
+
);
|
|
9283
|
+
if (!remainsCorner) return [];
|
|
9284
|
+
return [
|
|
9285
|
+
{
|
|
9061
9286
|
anchorPoint,
|
|
9062
9287
|
traceId: trace.mspPairId,
|
|
9063
9288
|
distance: getDistance(anchorPoint, label.anchorPoint),
|
|
9064
|
-
pinAligned
|
|
9065
|
-
|
|
9066
|
-
|
|
9289
|
+
pinAligned,
|
|
9290
|
+
reroutedTracePath
|
|
9291
|
+
}
|
|
9292
|
+
];
|
|
9293
|
+
});
|
|
9294
|
+
}
|
|
9295
|
+
isReroutedTraceClear(candidate, labelIndex) {
|
|
9296
|
+
const reroutedTracePath = candidate.reroutedTracePath;
|
|
9297
|
+
if (isPathCollidingWithObstacles(
|
|
9298
|
+
reroutedTracePath,
|
|
9299
|
+
getObstacleRects(this.inputProblem)
|
|
9300
|
+
)) {
|
|
9301
|
+
return false;
|
|
9302
|
+
}
|
|
9303
|
+
const candidateTrace = this.traceMap[candidate.traceId];
|
|
9304
|
+
const otherNetTraces = this.traces.filter(
|
|
9305
|
+
(trace) => trace.mspPairId !== candidate.traceId && trace.globalConnNetId !== candidateTrace.globalConnNetId
|
|
9306
|
+
);
|
|
9307
|
+
if (doesPathOverlapTraceStrokes(reroutedTracePath, otherNetTraces)) {
|
|
9308
|
+
return false;
|
|
9067
9309
|
}
|
|
9068
|
-
|
|
9310
|
+
const reroutedTraceMap = {
|
|
9311
|
+
[candidate.traceId]: {
|
|
9312
|
+
...candidateTrace,
|
|
9313
|
+
tracePath: reroutedTracePath
|
|
9314
|
+
}
|
|
9315
|
+
};
|
|
9316
|
+
return this.outputNetLabelPlacements.every((label, index) => {
|
|
9317
|
+
if (index === labelIndex) return true;
|
|
9318
|
+
return !traceCrossesBoundsInterior(
|
|
9319
|
+
getRectBounds(label.center, label.width, label.height),
|
|
9320
|
+
reroutedTraceMap
|
|
9321
|
+
);
|
|
9322
|
+
});
|
|
9069
9323
|
}
|
|
9070
9324
|
getTraceLinesForLabel(label) {
|
|
9071
9325
|
return this.traces.filter(
|
|
@@ -11380,9 +11634,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11380
11634
|
(instance) => [
|
|
11381
11635
|
{
|
|
11382
11636
|
inputProblem: instance.inputProblem,
|
|
11383
|
-
traces:
|
|
11384
|
-
instance.postLabelTraceOverlapShiftSolver.correctedTraceMap
|
|
11385
|
-
),
|
|
11637
|
+
traces: instance.railNetLabelCornerPlacementSolver.getOutput().traces,
|
|
11386
11638
|
netLabelPlacements: instance.railNetLabelCornerPlacementSolver.outputNetLabelPlacements
|
|
11387
11639
|
}
|
|
11388
11640
|
]
|
|
@@ -11393,9 +11645,7 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11393
11645
|
(instance) => [
|
|
11394
11646
|
{
|
|
11395
11647
|
inputProblem: instance.inputProblem,
|
|
11396
|
-
traces:
|
|
11397
|
-
instance.postLabelTraceOverlapShiftSolver.correctedTraceMap
|
|
11398
|
-
),
|
|
11648
|
+
traces: instance.railNetLabelCornerPlacementSolver.getOutput().traces,
|
|
11399
11649
|
netLabelPlacements: instance.traceAnchoredNetLabelOverlapSolver.outputNetLabelPlacements
|
|
11400
11650
|
}
|
|
11401
11651
|
]
|
|
@@ -11425,12 +11675,34 @@ var SchematicTracePipelineSolver = class extends BaseSolver {
|
|
|
11425
11675
|
"netLabelTraceCollisionSolver",
|
|
11426
11676
|
NetLabelTraceCollisionSolver,
|
|
11427
11677
|
(instance) => {
|
|
11428
|
-
const
|
|
11678
|
+
const previousOutput = instance.preAlignmentNetLabelTraceCollisionSolver.getOutput();
|
|
11679
|
+
const alignmentOutput = instance.traceCleanupSolver2.getOutput();
|
|
11680
|
+
const previousTraceMap = new Map(
|
|
11681
|
+
previousOutput.traces.map((trace) => [trace.mspPairId, trace])
|
|
11682
|
+
);
|
|
11683
|
+
let netLabelPlacements = previousOutput.netLabelPlacements;
|
|
11684
|
+
for (const trace of alignmentOutput.traces) {
|
|
11685
|
+
const previousTrace = previousTraceMap.get(trace.mspPairId);
|
|
11686
|
+
if (!previousTrace || previousTrace.tracePath === trace.tracePath) {
|
|
11687
|
+
continue;
|
|
11688
|
+
}
|
|
11689
|
+
const currentLabels = netLabelPlacements;
|
|
11690
|
+
const movedLabels = moveAttachedLabelsToReroutedTrace({
|
|
11691
|
+
trace: previousTrace,
|
|
11692
|
+
originalTracePath: previousTrace.tracePath,
|
|
11693
|
+
reroutedTracePath: trace.tracePath,
|
|
11694
|
+
netLabelPlacements: currentLabels
|
|
11695
|
+
});
|
|
11696
|
+
netLabelPlacements = movedLabels.map((movedLabel, labelIndex) => {
|
|
11697
|
+
const label = currentLabels[labelIndex];
|
|
11698
|
+
return label.orientation === "y+" || label.orientation === "y-" ? movedLabel : label;
|
|
11699
|
+
});
|
|
11700
|
+
}
|
|
11429
11701
|
return [
|
|
11430
11702
|
{
|
|
11431
11703
|
inputProblem: instance.inputProblem,
|
|
11432
|
-
traces:
|
|
11433
|
-
netLabelPlacements
|
|
11704
|
+
traces: alignmentOutput.traces,
|
|
11705
|
+
netLabelPlacements
|
|
11434
11706
|
}
|
|
11435
11707
|
];
|
|
11436
11708
|
}
|