@tscircuit/schematic-trace-solver 0.0.157 → 0.0.159
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 +1 -0
- package/dist/index.js +269 -58
- package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +15 -8
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +5 -0
- package/lib/solvers/MspConnectionPairSolver/getGroundConnectionPolicy.ts +83 -0
- package/lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts +6 -1
- package/lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts +196 -67
- package/lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts +122 -0
- package/package.json +1 -1
- package/site/bug-reports/bug-report-20260826T072956Z.page.tsx +4 -0
- package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T020342Z/__snapshots__/bug-report-20260707T020342Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T092615Z/__snapshots__/bug-report-20260707T092615Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T134549Z/__snapshots__/bug-report-20260707T134549Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T134722Z/__snapshots__/bug-report-20260707T134722Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T140410Z/__snapshots__/bug-report-20260707T140410Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T141025Z/__snapshots__/bug-report-20260707T141025Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T141421Z/__snapshots__/bug-report-20260707T141421Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260707T230831Z/__snapshots__/bug-report-20260707T230831Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260708T053736Z/__snapshots__/bug-report-20260708T053736Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260708T055430Z/__snapshots__/bug-report-20260708T055430Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260708T095725Z/__snapshots__/bug-report-20260708T095725Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260716T144856Z/__snapshots__/bug-report-20260716T144856Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260717T022934Z/__snapshots__/bug-report-20260717T022934Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260717T031704Z/__snapshots__/bug-report-20260717T031704Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260717T042845Z/__snapshots__/bug-report-20260717T042845Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260724T175257Z/__snapshots__/bug-report-20260724T175257Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +4 -4
- package/tests/bug-reports/bug-report-20260728T225606Z/__snapshots__/bug-report-20260728T225606Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260730T061837Z/__snapshots__/bug-report-20260730T061837Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260731T052229Z/__snapshots__/bug-report-20260731T052229Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260804T095800Z/__snapshots__/bug-report-20260804T095800Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260804T171919Z/__snapshots__/bug-report-20260804T171919Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260804T225912Z/__snapshots__/bug-report-20260804T225912Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260805T061316Z/__snapshots__/bug-report-20260805T061316Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260806T061548Z/__snapshots__/bug-report-20260806T061548Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260811T075142Z/__snapshots__/bug-report-20260811T075142Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260815T073240Z/__snapshots__/bug-report-20260815T073240Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260819T091818Z/__snapshots__/bug-report-20260819T091818Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260825T045913Z/__snapshots__/bug-report-20260825T045913Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260825T103621Z/__snapshots__/bug-report-20260825T103621Z.snap.svg +2 -2
- package/tests/bug-reports/bug-report-20260826T072956Z/__snapshots__/bug-report-20260826T072956Z.snap.svg +851 -0
- package/tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.json +2452 -0
- package/tests/bug-reports/bug-report-20260826T072956Z/bug-report-20260826T072956Z.test.ts +12 -0
- package/tests/examples/__snapshots__/example01.snap.svg +2 -2
- package/tests/examples/__snapshots__/example02.snap.svg +12 -12
- package/tests/examples/__snapshots__/example03.snap.svg +2 -2
- package/tests/examples/__snapshots__/example04.snap.svg +2 -2
- package/tests/examples/__snapshots__/example05.snap.svg +2 -2
- package/tests/examples/__snapshots__/example06.snap.svg +2 -2
- package/tests/examples/__snapshots__/example07.snap.svg +2 -2
- package/tests/examples/__snapshots__/example08.snap.svg +2 -2
- package/tests/examples/__snapshots__/example09.snap.svg +2 -2
- package/tests/examples/__snapshots__/example10.snap.svg +2 -2
- package/tests/examples/__snapshots__/example11.snap.svg +2 -2
- package/tests/examples/__snapshots__/example12.snap.svg +2 -2
- package/tests/examples/__snapshots__/example13.snap.svg +2 -2
- package/tests/examples/__snapshots__/example14.snap.svg +2 -2
- package/tests/examples/__snapshots__/example15.snap.svg +2 -2
- package/tests/examples/__snapshots__/example16.snap.svg +2 -2
- package/tests/examples/__snapshots__/example17.snap.svg +2 -2
- package/tests/examples/__snapshots__/example18.snap.svg +2 -2
- package/tests/examples/__snapshots__/example19.snap.svg +2 -2
- package/tests/examples/__snapshots__/example20.snap.svg +2 -2
- package/tests/examples/__snapshots__/example21.snap.svg +2 -2
- package/tests/examples/__snapshots__/example22.snap.svg +2 -2
- package/tests/examples/__snapshots__/example23.snap.svg +2 -2
- package/tests/examples/__snapshots__/example24.snap.svg +2 -2
- package/tests/examples/__snapshots__/example25.snap.svg +2 -2
- package/tests/examples/__snapshots__/example26.snap.svg +2 -2
- package/tests/examples/__snapshots__/example27.snap.svg +2 -2
- package/tests/examples/__snapshots__/example28.snap.svg +2 -2
- package/tests/examples/__snapshots__/example29.snap.svg +2 -2
- package/tests/examples/__snapshots__/example30.snap.svg +2 -2
- package/tests/examples/__snapshots__/example31.snap.svg +2 -2
- package/tests/examples/__snapshots__/example32.snap.svg +2 -2
- package/tests/examples/__snapshots__/example33.snap.svg +2 -2
- package/tests/examples/__snapshots__/example34.snap.svg +2 -2
- package/tests/examples/__snapshots__/example35.snap.svg +2 -2
- package/tests/examples/__snapshots__/example36.snap.svg +2 -2
- package/tests/examples/__snapshots__/example37.snap.svg +2 -2
- package/tests/examples/__snapshots__/example38.snap.svg +2 -2
- package/tests/examples/__snapshots__/example39.snap.svg +2 -2
- package/tests/examples/__snapshots__/example40.snap.svg +2 -2
- package/tests/examples/__snapshots__/example41.snap.svg +2 -2
- package/tests/examples/__snapshots__/example42.snap.svg +2 -2
- package/tests/examples/__snapshots__/example43.snap.svg +2 -2
- package/tests/examples/__snapshots__/example44.snap.svg +2 -2
- package/tests/examples/__snapshots__/example45.snap.svg +2 -2
- package/tests/examples/__snapshots__/example46.snap.svg +2 -2
- package/tests/examples/__snapshots__/example47.snap.svg +2 -2
- package/tests/examples/__snapshots__/example48.snap.svg +2 -2
- package/tests/examples/__snapshots__/example49.snap.svg +2 -2
- package/tests/examples/__snapshots__/example50.snap.svg +2 -2
- package/tests/examples/__snapshots__/example51.snap.svg +4 -4
- package/tests/fixtures/matcher.ts +3 -3
- package/tests/fixtures/parallel-ground-rail.ts +53 -0
- package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +4 -4
- package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +12 -12
- package/tests/repros/__snapshots__/board1096-usb-label-overlap-iteration.snap.svg +5 -5
- package/tests/repros/__snapshots__/bugreport-001-gnd-overlap.snap.svg +2 -2
- package/tests/repros/__snapshots__/manufacturePartNumber-text-box.snap.svg +2 -2
- 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-board-589-regulator-section.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-board-648-esp12f-section.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-bq24074-battery-charger.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-bq25895-cross-net-trace-overlap.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-cc2340r5.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-core-555-power-cross-net-overlap.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-core-subcircuit-missing-ground.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-core-tb67s579ftg-inline-label-routing.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-dc-power-isolated-routing.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-example35-minimize-trace-crossing.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-ground-label-on-lower-rail-end.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-ina237-current-monitor.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-missing-trace-netlabel.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-mspm0l1306-capacitor-symmetry.snap.svg +200 -0
- package/tests/repros/__snapshots__/repro-netlabel-collision-687.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-netlabel-overlap-trace.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-nrf52810-clock-routing.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-pga300-c6-trace-alignment.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-pga300-redundant-ground-traces.snap.svg +288 -0
- package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +4 -4
- package/tests/repros/__snapshots__/repro-power-group-rail-labels.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-rectifier-trace-overlap.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-rp2040-gamepad-trace-alignment.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-rp2040-usb-cc2-ground-overlap.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-rp2040-v3v3-label-placement.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-rp2040-zero-crystal-fallback-netlabels.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-ti-power-output-section.snap.svg +4 -4
- package/tests/repros/__snapshots__/repro-tida00553-j4-cell-divider.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-tida010076-j4-ground-bus.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-tps61222-trace-intersection.snap.svg +4 -4
- package/tests/repros/__snapshots__/repro-type-c-16pin-self-connection.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-type-c-16pin-to-resistor.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-type-c-vbus-led-ground-label.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-usb-power-vbus-label-detour.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro-vcc-pin1-detour.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro129-host-custom-symbol-passives.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro130-bq27441-fuel-gauge-trace-through-c1.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro161-power-section-autolayout.snap.svg +4 -4
- package/tests/repros/__snapshots__/repro47-endpoint-obstacle-detour.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro48-555-timer-vcc-rail-label.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro5-escape-padded-text-obstacles.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro51-overlap-junction-crossing.snap.svg +2 -2
- package/tests/repros/__snapshots__/repro84-stacked-gnd-label-orientation.snap.svg +2 -2
- package/tests/repros/__snapshots__/small-variant-resistor-facing-direction.snap.svg +2 -2
- package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +2 -2
- package/tests/repros/assets/repro-mspm0l1306-capacitor-symmetry.input.json +537 -0
- package/tests/repros/assets/repro-pga300-redundant-ground-traces.input.json +858 -0
- package/tests/repros/repro-mspm0l1306-capacitor-symmetry.test.ts +87 -0
- package/tests/repros/repro-pga300-redundant-ground-traces.test.ts +33 -0
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/anchored-net-label-width.snap.svg +2 -2
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-anchored-label-clearance.snap.svg +2 -2
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-port-stub.snap.svg +2 -2
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label-two-pin-net.snap.svg +2 -2
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label01.snap.svg +2 -2
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label02.snap.svg +2 -2
- package/tests/solvers/InlineNetLabelSolver/__snapshots__/terminal-stub-trace-clearance.snap.svg +2 -2
- package/tests/solvers/MspConnectionPairSolver/local-ground-branches.test.ts +105 -0
- package/tests/solvers/SameNetJunctionAlignmentSolver/ground-rail-label.test.ts +188 -0
- package/tests/solvers/SameNetJunctionAlignmentSolver/parallel-load-rails.test.ts +291 -0
- package/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/SchematicTraceSingleLineSolver2_01-example17-d1_1-u1_1.snap.svg +2 -2
- package/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/SchematicTraceSingleLineSolver2_repro47-short-opposing-pins.snap.svg +2 -2
- package/tests/solvers/TraceCleanupSolver/__snapshots__/TraceCleanupSolver.snap.svg +2 -2
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +2 -2
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg +2 -2
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg +2 -2
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -202,6 +202,7 @@ declare class MspConnectionPairSolver extends BaseSolver {
|
|
|
202
202
|
}>;
|
|
203
203
|
userNetIdByPinId: Record<string, string | undefined>;
|
|
204
204
|
directConnectionPinPairKeys: Set<string>;
|
|
205
|
+
private canRouteGroundPair;
|
|
205
206
|
constructor({ inputProblem }: {
|
|
206
207
|
inputProblem: InputProblem;
|
|
207
208
|
});
|
package/dist/index.js
CHANGED
|
@@ -363,6 +363,48 @@ var getConnectivityMapsFromInputProblem = (inputProblem) => {
|
|
|
363
363
|
return { directConnMap, netConnMap };
|
|
364
364
|
};
|
|
365
365
|
|
|
366
|
+
// lib/solvers/MspConnectionPairSolver/getGroundConnectionPolicy.ts
|
|
367
|
+
import { ConnectivityMap as ConnectivityMap2 } from "connectivity-map";
|
|
368
|
+
var MAX_LOCAL_GROUND_BRANCH_OFFSET = 1;
|
|
369
|
+
var getGroundConnectionPolicy = (inputProblem) => {
|
|
370
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
|
|
371
|
+
const groundNetId = netConnMap.getNetConnectedToId("GND");
|
|
372
|
+
if (!groundNetId) return () => true;
|
|
373
|
+
const physicalConnMap = new ConnectivityMap2({});
|
|
374
|
+
for (const connection of inputProblem.directConnections) {
|
|
375
|
+
physicalConnMap.addConnections([connection.pinIds]);
|
|
376
|
+
}
|
|
377
|
+
const pins = new Map(
|
|
378
|
+
inputProblem.chips.flatMap(
|
|
379
|
+
(chip) => chip.pins.map((pin) => [pin.pinId, { pin, chip }])
|
|
380
|
+
)
|
|
381
|
+
);
|
|
382
|
+
const groundFacingPins = [...pins.values()].filter(
|
|
383
|
+
({ pin, chip }) => chip.pins.length === 2 && netConnMap.getNetConnectedToId(pin.pinId) === groundNetId && (pin._facingDirection ?? getPinDirection(pin, chip)) === "y-"
|
|
384
|
+
);
|
|
385
|
+
const hasExplicitParallelGroundRail = groundFacingPins.some(({ pin: a }) => {
|
|
386
|
+
const group = physicalConnMap.getNetConnectedToId(a.pinId);
|
|
387
|
+
return group !== void 0 && groundFacingPins.some(
|
|
388
|
+
({ pin: b }) => a.pinId !== b.pinId && Math.abs(a.y - b.y) < 1e-6 && Math.abs(a.x - b.x) > 1e-6 && physicalConnMap.getNetConnectedToId(b.pinId) === group
|
|
389
|
+
);
|
|
390
|
+
});
|
|
391
|
+
if (!hasExplicitParallelGroundRail) return () => true;
|
|
392
|
+
const isGroundFacingTerminal = (pinId) => {
|
|
393
|
+
const entry = pins.get(pinId);
|
|
394
|
+
return entry?.chip.pins.length === 2 && !physicalConnMap.getNetConnectedToId(pinId) && (entry.pin._facingDirection ?? getPinDirection(entry.pin, entry.chip)) === "y-";
|
|
395
|
+
};
|
|
396
|
+
return (firstPinId, secondPinId) => {
|
|
397
|
+
if (!groundNetId || netConnMap.getNetConnectedToId(firstPinId) !== groundNetId || netConnMap.getNetConnectedToId(secondPinId) !== groundNetId) {
|
|
398
|
+
return true;
|
|
399
|
+
}
|
|
400
|
+
if (!isGroundFacingTerminal(firstPinId) && !isGroundFacingTerminal(secondPinId))
|
|
401
|
+
return true;
|
|
402
|
+
const first = pins.get(firstPinId)?.pin;
|
|
403
|
+
const second = pins.get(secondPinId)?.pin;
|
|
404
|
+
return !first || !second || Math.abs(first.y - second.y) <= MAX_LOCAL_GROUND_BRANCH_OFFSET + 1e-6;
|
|
405
|
+
};
|
|
406
|
+
};
|
|
407
|
+
|
|
366
408
|
// lib/solvers/MspConnectionPairSolver/getMspConnectionPairsFromPins.ts
|
|
367
409
|
function getOrthogonalMinimumSpanningTree(pins, opts = {}) {
|
|
368
410
|
const n = pins.length;
|
|
@@ -512,9 +554,11 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
512
554
|
pinMap;
|
|
513
555
|
userNetIdByPinId;
|
|
514
556
|
directConnectionPinPairKeys;
|
|
557
|
+
canRouteGroundPair;
|
|
515
558
|
constructor({ inputProblem }) {
|
|
516
559
|
super();
|
|
517
560
|
this.inputProblem = inputProblem;
|
|
561
|
+
this.canRouteGroundPair = getGroundConnectionPolicy(inputProblem);
|
|
518
562
|
this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
|
|
519
563
|
const { directConnMap, netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
|
|
520
564
|
this.dcConnMap = directConnMap;
|
|
@@ -571,6 +615,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
571
615
|
const [pin1, pin2] = directlyConnectedPins;
|
|
572
616
|
const p1 = this.pinMap[pin1];
|
|
573
617
|
const p2 = this.pinMap[pin2];
|
|
618
|
+
if (!this.canRouteGroundPair(pin1, pin2)) return;
|
|
574
619
|
const pinPairKey = getPinPairKey([pin1, pin2]);
|
|
575
620
|
let pairDistance = Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
|
|
576
621
|
if (this.directConnectionPinPairKeys.has(pinPairKey)) {
|
|
@@ -615,7 +660,7 @@ var MspConnectionPairSolver = class extends BaseSolver {
|
|
|
615
660
|
directlyConnectedPins.map((p) => this.pinMap[p]).filter(Boolean),
|
|
616
661
|
{
|
|
617
662
|
maxDistance: this.maxMspPairDistance,
|
|
618
|
-
forbidEdge: (a, b) => arePinsInDifferentSchematicSections(
|
|
663
|
+
forbidEdge: (a, b) => !this.canRouteGroundPair(a.pinId, b.pinId) || arePinsInDifferentSchematicSections(
|
|
619
664
|
this.inputProblem,
|
|
620
665
|
a,
|
|
621
666
|
b
|
|
@@ -4827,6 +4872,7 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4827
4872
|
super();
|
|
4828
4873
|
this.params = params;
|
|
4829
4874
|
const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } = this.params;
|
|
4875
|
+
const canRouteGroundPair = getGroundConnectionPolicy(inputProblem);
|
|
4830
4876
|
this.inputProblem = inputProblem;
|
|
4831
4877
|
this.allSolvedTraces = [...alreadySolvedTraces];
|
|
4832
4878
|
this.maxMspPairDistance = inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE;
|
|
@@ -4845,7 +4891,10 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4845
4891
|
}
|
|
4846
4892
|
}
|
|
4847
4893
|
this.queuedFailedConnectionPairs = this.params.failedConnectionPairs.filter(
|
|
4848
|
-
(connectionPair) =>
|
|
4894
|
+
(connectionPair) => canRouteGroundPair(
|
|
4895
|
+
connectionPair.pins[0].pinId,
|
|
4896
|
+
connectionPair.pins[1].pinId
|
|
4897
|
+
) && isLabeledPeripheralConnection({
|
|
4849
4898
|
inputProblem: this.inputProblem,
|
|
4850
4899
|
chipMap: this.chipMap,
|
|
4851
4900
|
pins: connectionPair.pins
|
|
@@ -4865,6 +4914,7 @@ var LongDistancePairSolver = class extends BaseSolver {
|
|
|
4865
4914
|
const neighbors = allPinIdsInNet.filter((otherPinId) => otherPinId !== unconnectedPinId).flatMap((otherPinId) => {
|
|
4866
4915
|
const targetPin = pinMap.get(otherPinId);
|
|
4867
4916
|
if (!targetPin) return [];
|
|
4917
|
+
if (!canRouteGroundPair(sourcePin.pinId, targetPin.pinId)) return [];
|
|
4868
4918
|
const isNamedTwoPinConnection = inputProblem.netConnections.some(
|
|
4869
4919
|
(connection) => connection.pinIds.length === 2 && connection.pinIds.includes(sourcePin.pinId) && connection.pinIds.includes(targetPin.pinId)
|
|
4870
4920
|
);
|
|
@@ -12322,6 +12372,9 @@ var UnroutedTraceRecoverySolver = class extends BaseSolver {
|
|
|
12322
12372
|
}
|
|
12323
12373
|
};
|
|
12324
12374
|
|
|
12375
|
+
// lib/solvers/SameNetJunctionAlignmentSolver/alignSameNetJunctions.ts
|
|
12376
|
+
import { getSegmentIntersection as getSegmentIntersection3 } from "@tscircuit/math-utils/line-intersections";
|
|
12377
|
+
|
|
12325
12378
|
// lib/solvers/SameNetJunctionAlignmentSolver/pathIntersectsAnyNetLabel.ts
|
|
12326
12379
|
var NET_LABEL_INTERIOR_INSET = 1e-6;
|
|
12327
12380
|
var pathIntersectsAnyNetLabel = ({
|
|
@@ -12363,6 +12416,7 @@ var pathEntersAnyNetLabel = ({
|
|
|
12363
12416
|
var MAX_ALIGNED_LOAD_PIN_OFFSET = 0.2;
|
|
12364
12417
|
var MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET = 0.2;
|
|
12365
12418
|
var MAX_SHARED_PIN_RAIL_OFFSET = 0.05;
|
|
12419
|
+
var MIN_RETURN_STEM_LENGTH = 0.05;
|
|
12366
12420
|
var getSharedPin = ({
|
|
12367
12421
|
donorTrace,
|
|
12368
12422
|
branchTrace
|
|
@@ -12400,6 +12454,7 @@ var railIsOnFacingSide = ({
|
|
|
12400
12454
|
pin
|
|
12401
12455
|
}) => {
|
|
12402
12456
|
if (pin._facingDirection === "y+") return railY > pin.y;
|
|
12457
|
+
if (pin._facingDirection === "y-") return railY < pin.y;
|
|
12403
12458
|
return false;
|
|
12404
12459
|
};
|
|
12405
12460
|
var railIsOnSharedPinFacingSide = ({
|
|
@@ -12532,6 +12587,27 @@ var candidateIsClear = ({
|
|
|
12532
12587
|
if (doesPathCoincideWithTraces(candidateTrace.tracePath, otherNetTraces)) {
|
|
12533
12588
|
return false;
|
|
12534
12589
|
}
|
|
12590
|
+
for (const otherTrace of otherNetTraces) {
|
|
12591
|
+
const countIntersections = (path) => {
|
|
12592
|
+
const intersections = /* @__PURE__ */ new Set();
|
|
12593
|
+
for (let i = 1; i < path.length; i++) {
|
|
12594
|
+
for (let j = 1; j < otherTrace.tracePath.length; j++) {
|
|
12595
|
+
const point = getSegmentIntersection3(
|
|
12596
|
+
path[i - 1],
|
|
12597
|
+
path[i],
|
|
12598
|
+
otherTrace.tracePath[j - 1],
|
|
12599
|
+
otherTrace.tracePath[j]
|
|
12600
|
+
);
|
|
12601
|
+
if (point)
|
|
12602
|
+
intersections.add(`${point.x.toFixed(6)},${point.y.toFixed(6)}`);
|
|
12603
|
+
}
|
|
12604
|
+
}
|
|
12605
|
+
return intersections.size;
|
|
12606
|
+
};
|
|
12607
|
+
if (countIntersections(candidateTrace.tracePath) > countIntersections(originalTrace.tracePath)) {
|
|
12608
|
+
return false;
|
|
12609
|
+
}
|
|
12610
|
+
}
|
|
12535
12611
|
if (!attachedLabelIndexes.every(
|
|
12536
12612
|
(index) => tracePathContainsPoint(
|
|
12537
12613
|
candidateTrace.tracePath,
|
|
@@ -12569,6 +12645,57 @@ var candidateIsClear = ({
|
|
|
12569
12645
|
if (!originalRail || !candidateRail) return false;
|
|
12570
12646
|
return Math.abs(originalRail.start.y - candidateRail.start.y) <= MAX_SAME_NET_LABEL_BOUNDARY_RAIL_OFFSET;
|
|
12571
12647
|
};
|
|
12648
|
+
var getAlignedReturnBranchPath = ({
|
|
12649
|
+
donorTrace,
|
|
12650
|
+
branchTrace,
|
|
12651
|
+
returnStemScale = 1
|
|
12652
|
+
}) => {
|
|
12653
|
+
const sharedPin = getSharedPin({ donorTrace, branchTrace });
|
|
12654
|
+
if (!sharedPin) return null;
|
|
12655
|
+
const railPin = getOtherPin({ trace: donorTrace, sharedPin });
|
|
12656
|
+
const returnPin = getOtherPin({ trace: branchTrace, sharedPin });
|
|
12657
|
+
if (!railPin || !returnPin) return null;
|
|
12658
|
+
const facing = sharedPin._facingDirection;
|
|
12659
|
+
if (facing !== "y+" && facing !== "y-") return null;
|
|
12660
|
+
if (railPin._facingDirection !== facing || returnPin._facingDirection !== facing)
|
|
12661
|
+
return null;
|
|
12662
|
+
if (!nearlyEqual(sharedPin.y, railPin.y)) return null;
|
|
12663
|
+
const donorPath = simplifyPath(donorTrace.tracePath);
|
|
12664
|
+
if (donorPath.length !== 4) return null;
|
|
12665
|
+
const rail = getLongestHorizontalSegment(donorTrace);
|
|
12666
|
+
if (!rail || !railIsOnFacingSide({ railY: rail.start.y, pin: sharedPin }))
|
|
12667
|
+
return null;
|
|
12668
|
+
if (!isVertical2(donorPath[0], donorPath[1]) || !isVertical2(donorPath[2], donorPath[3]))
|
|
12669
|
+
return null;
|
|
12670
|
+
const sharedFirst = branchTrace.pins[0].pinId === sharedPin.pinId;
|
|
12671
|
+
const path = simplifyPath(
|
|
12672
|
+
sharedFirst ? branchTrace.tracePath : [...branchTrace.tracePath].reverse()
|
|
12673
|
+
);
|
|
12674
|
+
if (path.length !== 6) return null;
|
|
12675
|
+
if (!isVertical2(path[0], path[1]) || !isHorizontal2(path[1], path[2]) || !isVertical2(path[2], path[3]) || !isHorizontal2(path[3], path[4]) || !isVertical2(path[4], path[5]))
|
|
12676
|
+
return null;
|
|
12677
|
+
if (!railIsOnFacingSide({ railY: path[1].y, pin: sharedPin }) || !railIsOnFacingSide({ railY: path[4].y, pin: returnPin }))
|
|
12678
|
+
return null;
|
|
12679
|
+
const direction = facing === "y+" ? 1 : -1;
|
|
12680
|
+
if (direction * (returnPin.y - rail.start.y) <= 0) return null;
|
|
12681
|
+
const minX = Math.min(sharedPin.x, railPin.x);
|
|
12682
|
+
const maxX = Math.max(sharedPin.x, railPin.x);
|
|
12683
|
+
if (path[2].x < minX || path[2].x > maxX) return null;
|
|
12684
|
+
if (nearlyEqual(path[2].x, railPin.x) && nearlyEqual(path[1].y, rail.start.y))
|
|
12685
|
+
return null;
|
|
12686
|
+
const returnRailY = returnPin.y + (path[4].y - returnPin.y) * returnStemScale;
|
|
12687
|
+
if (Math.abs(returnRailY - returnPin.y) < MIN_RETURN_STEM_LENGTH - 1e-6)
|
|
12688
|
+
return null;
|
|
12689
|
+
const candidate = simplifyPath([
|
|
12690
|
+
{ x: sharedPin.x, y: sharedPin.y },
|
|
12691
|
+
{ x: sharedPin.x, y: rail.start.y },
|
|
12692
|
+
{ x: railPin.x, y: rail.start.y },
|
|
12693
|
+
{ x: railPin.x, y: returnRailY },
|
|
12694
|
+
{ x: returnPin.x, y: returnRailY },
|
|
12695
|
+
{ x: returnPin.x, y: returnPin.y }
|
|
12696
|
+
]);
|
|
12697
|
+
return sharedFirst ? candidate : candidate.reverse();
|
|
12698
|
+
};
|
|
12572
12699
|
var alignSameNetJunctions = ({
|
|
12573
12700
|
inputProblem,
|
|
12574
12701
|
traces,
|
|
@@ -12576,63 +12703,74 @@ var alignSameNetJunctions = ({
|
|
|
12576
12703
|
}) => {
|
|
12577
12704
|
let outputTraces = [...traces];
|
|
12578
12705
|
let outputNetLabelPlacements = [...netLabelPlacements];
|
|
12579
|
-
const alignedBranchTraceIds = /* @__PURE__ */ new Set();
|
|
12580
12706
|
let alignedJunctionCount = 0;
|
|
12581
|
-
const
|
|
12582
|
-
|
|
12583
|
-
|
|
12584
|
-
const
|
|
12585
|
-
|
|
12586
|
-
|
|
12587
|
-
(
|
|
12588
|
-
|
|
12589
|
-
|
|
12590
|
-
if (alignedBranchTraceIds.has(branchTrace.mspPairId)) continue;
|
|
12591
|
-
if (donorTrace.mspPairId === branchTrace.mspPairId) continue;
|
|
12592
|
-
if (donorTrace.globalConnNetId !== branchTrace.globalConnNetId) continue;
|
|
12593
|
-
const candidatePath = getAlignedBranchPath({ donorTrace, branchTrace });
|
|
12594
|
-
if (!candidatePath) continue;
|
|
12595
|
-
const candidateTrace = { ...branchTrace, tracePath: candidatePath };
|
|
12596
|
-
const originalPair = [donorTrace, branchTrace];
|
|
12597
|
-
const candidatePair = [donorTrace, candidateTrace];
|
|
12598
|
-
const removesVisibleSegment = getVisibleTraceSegmentCount(candidatePair) < getVisibleTraceSegmentCount(originalPair);
|
|
12599
|
-
const shortensVisibleTrace = getVisibleTraceLength(candidatePair) < getVisibleTraceLength(originalPair) && !nearlyEqual(
|
|
12600
|
-
getVisibleTraceLength(candidatePair),
|
|
12601
|
-
getVisibleTraceLength(originalPair)
|
|
12707
|
+
for (const alignReturnBranches of [false, true]) {
|
|
12708
|
+
const alignedBranchTraceIds = /* @__PURE__ */ new Set();
|
|
12709
|
+
const donorTraceIds = traces.map((trace) => trace.mspPairId);
|
|
12710
|
+
const pendingDonorTraceIds = new Set(donorTraceIds);
|
|
12711
|
+
for (let donorIndex = 0; donorIndex < donorTraceIds.length; donorIndex++) {
|
|
12712
|
+
const donorTraceId = donorTraceIds[donorIndex];
|
|
12713
|
+
pendingDonorTraceIds.delete(donorTraceId);
|
|
12714
|
+
const donorTrace = outputTraces.find(
|
|
12715
|
+
(trace) => trace.mspPairId === donorTraceId
|
|
12602
12716
|
);
|
|
12603
|
-
|
|
12604
|
-
continue;
|
|
12605
|
-
|
|
12606
|
-
|
|
12607
|
-
|
|
12608
|
-
|
|
12609
|
-
|
|
12610
|
-
|
|
12611
|
-
|
|
12612
|
-
|
|
12613
|
-
|
|
12614
|
-
|
|
12615
|
-
|
|
12616
|
-
|
|
12617
|
-
|
|
12618
|
-
|
|
12619
|
-
|
|
12620
|
-
|
|
12621
|
-
|
|
12622
|
-
|
|
12623
|
-
|
|
12624
|
-
|
|
12625
|
-
|
|
12626
|
-
|
|
12627
|
-
|
|
12628
|
-
|
|
12629
|
-
|
|
12630
|
-
|
|
12631
|
-
|
|
12632
|
-
|
|
12633
|
-
|
|
12634
|
-
|
|
12635
|
-
|
|
12717
|
+
for (const branchTrace of outputTraces) {
|
|
12718
|
+
if (alignedBranchTraceIds.has(branchTrace.mspPairId)) continue;
|
|
12719
|
+
if (donorTrace.mspPairId === branchTrace.mspPairId) continue;
|
|
12720
|
+
if (donorTrace.globalConnNetId !== branchTrace.globalConnNetId) continue;
|
|
12721
|
+
const candidatePaths = alignReturnBranches ? [1, 0.5, 0.25].map(
|
|
12722
|
+
(returnStemScale) => getAlignedReturnBranchPath({
|
|
12723
|
+
donorTrace,
|
|
12724
|
+
branchTrace,
|
|
12725
|
+
returnStemScale
|
|
12726
|
+
})
|
|
12727
|
+
) : [getAlignedBranchPath({ donorTrace, branchTrace })];
|
|
12728
|
+
for (const candidatePath of candidatePaths) {
|
|
12729
|
+
if (!candidatePath) continue;
|
|
12730
|
+
const candidateTrace = { ...branchTrace, tracePath: candidatePath };
|
|
12731
|
+
const originalPair = [donorTrace, branchTrace];
|
|
12732
|
+
const candidatePair = [donorTrace, candidateTrace];
|
|
12733
|
+
const removesVisibleSegment = getVisibleTraceSegmentCount(candidatePair) < getVisibleTraceSegmentCount(originalPair);
|
|
12734
|
+
const shortensVisibleTrace = getVisibleTraceLength(candidatePair) < getVisibleTraceLength(originalPair) && !nearlyEqual(
|
|
12735
|
+
getVisibleTraceLength(candidatePair),
|
|
12736
|
+
getVisibleTraceLength(originalPair)
|
|
12737
|
+
);
|
|
12738
|
+
if (!removesVisibleSegment && !shortensVisibleTrace) {
|
|
12739
|
+
continue;
|
|
12740
|
+
}
|
|
12741
|
+
const attachedLabelIndexes = getAttachedLabelIndexes(
|
|
12742
|
+
branchTrace,
|
|
12743
|
+
outputNetLabelPlacements
|
|
12744
|
+
);
|
|
12745
|
+
const candidateNetLabelPlacements = moveAttachedLabels({
|
|
12746
|
+
trace: branchTrace,
|
|
12747
|
+
reroutedTracePath: candidatePath,
|
|
12748
|
+
netLabelPlacements: outputNetLabelPlacements,
|
|
12749
|
+
attachedLabelIndexes
|
|
12750
|
+
});
|
|
12751
|
+
if (!candidateIsClear({
|
|
12752
|
+
candidateTrace,
|
|
12753
|
+
originalTrace: branchTrace,
|
|
12754
|
+
traces: outputTraces,
|
|
12755
|
+
inputProblem,
|
|
12756
|
+
candidateNetLabelPlacements,
|
|
12757
|
+
attachedLabelIndexes
|
|
12758
|
+
})) {
|
|
12759
|
+
continue;
|
|
12760
|
+
}
|
|
12761
|
+
outputTraces = outputTraces.map((trace) => {
|
|
12762
|
+
if (trace.mspPairId === branchTrace.mspPairId) return candidateTrace;
|
|
12763
|
+
return trace;
|
|
12764
|
+
});
|
|
12765
|
+
outputNetLabelPlacements = candidateNetLabelPlacements;
|
|
12766
|
+
alignedBranchTraceIds.add(branchTrace.mspPairId);
|
|
12767
|
+
alignedJunctionCount++;
|
|
12768
|
+
if (!pendingDonorTraceIds.has(branchTrace.mspPairId)) {
|
|
12769
|
+
donorTraceIds.push(branchTrace.mspPairId);
|
|
12770
|
+
pendingDonorTraceIds.add(branchTrace.mspPairId);
|
|
12771
|
+
}
|
|
12772
|
+
break;
|
|
12773
|
+
}
|
|
12636
12774
|
}
|
|
12637
12775
|
}
|
|
12638
12776
|
}
|
|
@@ -12643,6 +12781,75 @@ var alignSameNetJunctions = ({
|
|
|
12643
12781
|
};
|
|
12644
12782
|
};
|
|
12645
12783
|
|
|
12784
|
+
// lib/solvers/SameNetJunctionAlignmentSolver/placeGroundRailLabelsAtOuterEnd.ts
|
|
12785
|
+
var placeGroundRailLabelsAtOuterEnd = ({
|
|
12786
|
+
inputProblem,
|
|
12787
|
+
traces,
|
|
12788
|
+
netLabelPlacements
|
|
12789
|
+
}) => {
|
|
12790
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem);
|
|
12791
|
+
const groundNetId = netConnMap.getNetConnectedToId("GND");
|
|
12792
|
+
if (!groundNetId) return netLabelPlacements;
|
|
12793
|
+
const chipMap = new Map(inputProblem.chips.map((chip) => [chip.chipId, chip]));
|
|
12794
|
+
const traceMap = Object.fromEntries(
|
|
12795
|
+
traces.map((trace) => [trace.mspPairId, trace])
|
|
12796
|
+
);
|
|
12797
|
+
const obstacles = getObstacleRects(inputProblem);
|
|
12798
|
+
const output = [...netLabelPlacements];
|
|
12799
|
+
for (const rail of traces) {
|
|
12800
|
+
if (rail.globalConnNetId !== groundNetId) continue;
|
|
12801
|
+
const [a, b] = rail.pins;
|
|
12802
|
+
if (a.chipId === b.chipId || !nearlyEqual(a.y, b.y) || ![a, b].every(
|
|
12803
|
+
(pin) => pin._facingDirection === "y-" && chipMap.get(pin.chipId)?.pins.length === 2
|
|
12804
|
+
))
|
|
12805
|
+
continue;
|
|
12806
|
+
const path = simplifyPath(rail.tracePath);
|
|
12807
|
+
if (path.length !== 4 || !isVertical2(path[0], path[1]) || !isHorizontal2(path[1], path[2]) || !isVertical2(path[2], path[3]) || path[1].y >= a.y)
|
|
12808
|
+
continue;
|
|
12809
|
+
for (const feed of traces) {
|
|
12810
|
+
if (feed.globalConnNetId !== groundNetId) continue;
|
|
12811
|
+
const shared = feed.pins.find((pin) => rail.pinIds.includes(pin.pinId));
|
|
12812
|
+
const icPin = feed.pins.find(
|
|
12813
|
+
(pin) => !rail.pinIds.includes(pin.pinId) && (chipMap.get(pin.chipId)?.pins.length ?? 0) > 2
|
|
12814
|
+
);
|
|
12815
|
+
if (!shared || !icPin || !nearlyEqual(icPin.y, path[1].y)) continue;
|
|
12816
|
+
const outer = shared.pinId === a.pinId ? b : a;
|
|
12817
|
+
if (Math.abs(outer.x - icPin.x) <= Math.abs(shared.x - icPin.x)) continue;
|
|
12818
|
+
const anchorPoint = { x: outer.x, y: path[1].y };
|
|
12819
|
+
if (!tracePathContainsPoint(rail.tracePath, anchorPoint)) continue;
|
|
12820
|
+
for (let index = 0; index < output.length; index++) {
|
|
12821
|
+
const label = output[index];
|
|
12822
|
+
if (label.globalConnNetId !== groundNetId || label.orientation !== "y-" || !label.mspConnectionPairIds.some(
|
|
12823
|
+
(id) => id === feed.mspPairId || id === rail.mspPairId
|
|
12824
|
+
) || !tracePathContainsPoint(feed.tracePath, label.anchorPoint) && !tracePathContainsPoint(rail.tracePath, label.anchorPoint))
|
|
12825
|
+
continue;
|
|
12826
|
+
const center = getCenterFromAnchor(
|
|
12827
|
+
anchorPoint,
|
|
12828
|
+
label.orientation,
|
|
12829
|
+
label.width,
|
|
12830
|
+
label.height
|
|
12831
|
+
);
|
|
12832
|
+
const bounds = getRectBounds(center, label.width, label.height);
|
|
12833
|
+
if (obstacles.some((obstacle) => rectsOverlap(bounds, obstacle)) || traceCrossesBoundsInterior(bounds, traceMap) || output.some(
|
|
12834
|
+
(other, otherIndex) => otherIndex !== index && rectsOverlap(
|
|
12835
|
+
bounds,
|
|
12836
|
+
getRectBounds(other.center, other.width, other.height)
|
|
12837
|
+
)
|
|
12838
|
+
))
|
|
12839
|
+
continue;
|
|
12840
|
+
output[index] = {
|
|
12841
|
+
...label,
|
|
12842
|
+
anchorPoint,
|
|
12843
|
+
center,
|
|
12844
|
+
mspConnectionPairIds: [rail.mspPairId],
|
|
12845
|
+
pinIds: [...rail.pinIds]
|
|
12846
|
+
};
|
|
12847
|
+
}
|
|
12848
|
+
}
|
|
12849
|
+
}
|
|
12850
|
+
return output;
|
|
12851
|
+
};
|
|
12852
|
+
|
|
12646
12853
|
// lib/solvers/SameNetJunctionAlignmentSolver/SameNetJunctionAlignmentSolver.ts
|
|
12647
12854
|
var SameNetJunctionAlignmentSolver = class extends BaseSolver {
|
|
12648
12855
|
input;
|
|
@@ -12657,7 +12864,11 @@ var SameNetJunctionAlignmentSolver = class extends BaseSolver {
|
|
|
12657
12864
|
_step() {
|
|
12658
12865
|
const result = alignSameNetJunctions(this.input);
|
|
12659
12866
|
this.outputTraces = result.traces;
|
|
12660
|
-
this.outputNetLabelPlacements =
|
|
12867
|
+
this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
|
|
12868
|
+
inputProblem: this.input.inputProblem,
|
|
12869
|
+
traces: result.traces,
|
|
12870
|
+
netLabelPlacements: result.netLabelPlacements
|
|
12871
|
+
});
|
|
12661
12872
|
this.stats.alignedJunctionCount = result.alignedJunctionCount;
|
|
12662
12873
|
this.solved = true;
|
|
12663
12874
|
}
|
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
2
1
|
import type { Point } from "@tscircuit/math-utils"
|
|
2
|
+
import type { ConnectivityMap } from "connectivity-map"
|
|
3
|
+
import { getConnectivityMapsFromInputProblem } from "lib/solvers/MspConnectionPairSolver/getConnectivityMapFromInputProblem"
|
|
3
4
|
import {
|
|
4
5
|
DEFAULT_MAX_MSP_PAIR_DISTANCE,
|
|
5
6
|
type MspConnectionPair,
|
|
6
7
|
} from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
|
|
7
8
|
import type {
|
|
8
|
-
|
|
9
|
+
InputChip,
|
|
9
10
|
InputPin,
|
|
11
|
+
InputProblem,
|
|
10
12
|
PinId,
|
|
11
|
-
InputChip,
|
|
12
13
|
} from "lib/types/InputProblem"
|
|
13
|
-
import { BaseSolver } from "../BaseSolver/BaseSolver"
|
|
14
|
-
import { SchematicTraceSingleLineSolver2 } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
|
|
15
|
-
import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"
|
|
16
|
-
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
17
|
-
import type { ConnectivityMap } from "connectivity-map"
|
|
18
14
|
import { doesTraceOverlapWithExistingTraces } from "lib/utils/does-trace-overlap-with-existing-traces"
|
|
19
15
|
import { arePinsInDifferentSchematicSections } from "../../utils/arePinsInDifferentSchematicSections"
|
|
16
|
+
import { BaseSolver } from "../BaseSolver/BaseSolver"
|
|
17
|
+
import { getGroundConnectionPolicy } from "../MspConnectionPairSolver/getGroundConnectionPolicy"
|
|
20
18
|
import { isLabeledPeripheralConnection } from "../MspConnectionPairSolver/isLabeledPeripheralConnection"
|
|
19
|
+
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
20
|
+
import { SchematicTraceSingleLineSolver2 } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
|
|
21
|
+
import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"
|
|
21
22
|
|
|
22
23
|
const NEAREST_NEIGHBOR_COUNT = 3
|
|
23
24
|
|
|
@@ -163,6 +164,7 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
163
164
|
|
|
164
165
|
const { inputProblem, primaryMspConnectionPairs, alreadySolvedTraces } =
|
|
165
166
|
this.params
|
|
167
|
+
const canRouteGroundPair = getGroundConnectionPolicy(inputProblem)
|
|
166
168
|
|
|
167
169
|
this.inputProblem = inputProblem
|
|
168
170
|
this.allSolvedTraces = [...alreadySolvedTraces]
|
|
@@ -189,6 +191,10 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
189
191
|
// new nearest-neighbor candidates.
|
|
190
192
|
this.queuedFailedConnectionPairs = this.params.failedConnectionPairs.filter(
|
|
191
193
|
(connectionPair) =>
|
|
194
|
+
canRouteGroundPair(
|
|
195
|
+
connectionPair.pins[0].pinId,
|
|
196
|
+
connectionPair.pins[1].pinId,
|
|
197
|
+
) &&
|
|
192
198
|
isLabeledPeripheralConnection({
|
|
193
199
|
inputProblem: this.inputProblem,
|
|
194
200
|
chipMap: this.chipMap,
|
|
@@ -219,6 +225,7 @@ export class LongDistancePairSolver extends BaseSolver {
|
|
|
219
225
|
.flatMap((otherPinId) => {
|
|
220
226
|
const targetPin = pinMap.get(otherPinId)
|
|
221
227
|
if (!targetPin) return [] // Gracefully handle missing pins
|
|
228
|
+
if (!canRouteGroundPair(sourcePin.pinId, targetPin.pinId)) return []
|
|
222
229
|
const isNamedTwoPinConnection = inputProblem.netConnections.some(
|
|
223
230
|
(connection) =>
|
|
224
231
|
connection.pinIds.length === 2 &&
|
|
@@ -13,6 +13,7 @@ import { arePinsInDifferentSchematicSections } from "../../utils/arePinsInDiffer
|
|
|
13
13
|
import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"
|
|
14
14
|
import { doesPairCrossRestrictedCenterLines } from "./doesPairCrossRestrictedCenterLines"
|
|
15
15
|
import { getConnectivityMapsFromInputProblem } from "./getConnectivityMapFromInputProblem"
|
|
16
|
+
import { getGroundConnectionPolicy } from "./getGroundConnectionPolicy"
|
|
16
17
|
import { getOrthogonalMinimumSpanningTree } from "./getMspConnectionPairsFromPins"
|
|
17
18
|
import { getLabeledConnectionRouteReason } from "./isLabeledPeripheralConnection"
|
|
18
19
|
|
|
@@ -45,11 +46,13 @@ export class MspConnectionPairSolver extends BaseSolver {
|
|
|
45
46
|
pinMap: Record<string, InputPin & { chipId: string }>
|
|
46
47
|
userNetIdByPinId: Record<string, string | undefined>
|
|
47
48
|
directConnectionPinPairKeys: Set<string>
|
|
49
|
+
private canRouteGroundPair: (firstPinId: PinId, secondPinId: PinId) => boolean
|
|
48
50
|
|
|
49
51
|
constructor({ inputProblem }: { inputProblem: InputProblem }) {
|
|
50
52
|
super()
|
|
51
53
|
|
|
52
54
|
this.inputProblem = inputProblem
|
|
55
|
+
this.canRouteGroundPair = getGroundConnectionPolicy(inputProblem)
|
|
53
56
|
this.maxMspPairDistance =
|
|
54
57
|
inputProblem.maxMspPairDistance ?? DEFAULT_MAX_MSP_PAIR_DISTANCE
|
|
55
58
|
|
|
@@ -124,6 +127,7 @@ export class MspConnectionPairSolver extends BaseSolver {
|
|
|
124
127
|
const [pin1, pin2] = directlyConnectedPins
|
|
125
128
|
const p1 = this.pinMap[pin1!]!
|
|
126
129
|
const p2 = this.pinMap[pin2!]!
|
|
130
|
+
if (!this.canRouteGroundPair(pin1!, pin2!)) return
|
|
127
131
|
const pinPairKey = getPinPairKey([pin1!, pin2!])
|
|
128
132
|
// Explicit source traces are classified by straight-line distance when
|
|
129
133
|
// their input is created; named nets retain the orthogonal route metric.
|
|
@@ -195,6 +199,7 @@ export class MspConnectionPairSolver extends BaseSolver {
|
|
|
195
199
|
{
|
|
196
200
|
maxDistance: this.maxMspPairDistance,
|
|
197
201
|
forbidEdge: (a, b) =>
|
|
202
|
+
!this.canRouteGroundPair(a.pinId, b.pinId) ||
|
|
198
203
|
arePinsInDifferentSchematicSections(
|
|
199
204
|
this.inputProblem,
|
|
200
205
|
a as InputPin & { chipId: string },
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { ConnectivityMap } from "connectivity-map"
|
|
2
|
+
import type { InputProblem, PinId } from "lib/types/InputProblem"
|
|
3
|
+
import { getPinDirection } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection"
|
|
4
|
+
import { getConnectivityMapsFromInputProblem } from "./getConnectivityMapFromInputProblem"
|
|
5
|
+
|
|
6
|
+
// Keep nearby ground connections, but do not extend the usual 1 mm local
|
|
7
|
+
// routing range vertically just because a sheet allows long signal traces.
|
|
8
|
+
const MAX_LOCAL_GROUND_BRANCH_OFFSET = 1
|
|
9
|
+
|
|
10
|
+
/** Avoid return wires between staggered, net-only ground-facing branches. */
|
|
11
|
+
export const getGroundConnectionPolicy = (inputProblem: InputProblem) => {
|
|
12
|
+
const { netConnMap } = getConnectivityMapsFromInputProblem(inputProblem)
|
|
13
|
+
const groundNetId = netConnMap.getNetConnectedToId("GND")
|
|
14
|
+
if (!groundNetId) return () => true
|
|
15
|
+
// Net identifiers do not constitute physical edges: two separate direct
|
|
16
|
+
// connections may have the same netId without requesting a wire between them.
|
|
17
|
+
const physicalConnMap = new ConnectivityMap({})
|
|
18
|
+
for (const connection of inputProblem.directConnections) {
|
|
19
|
+
physicalConnMap.addConnections([connection.pinIds])
|
|
20
|
+
}
|
|
21
|
+
const pins = new Map(
|
|
22
|
+
inputProblem.chips.flatMap((chip) =>
|
|
23
|
+
chip.pins.map((pin) => [pin.pinId, { pin, chip }] as const),
|
|
24
|
+
),
|
|
25
|
+
)
|
|
26
|
+
const groundFacingPins = [...pins.values()].filter(
|
|
27
|
+
({ pin, chip }) =>
|
|
28
|
+
chip.pins.length === 2 &&
|
|
29
|
+
netConnMap.getNetConnectedToId(pin.pinId) === groundNetId &&
|
|
30
|
+
(pin._facingDirection ?? getPinDirection(pin, chip)) === "y-",
|
|
31
|
+
)
|
|
32
|
+
// A deliberately wired, level bank of two-pin loads already has its own
|
|
33
|
+
// shared return rail. Do not extend that rail to independent lower branches
|
|
34
|
+
// just to reduce the number of GND symbols. Other ground topologies retain
|
|
35
|
+
// their existing routing behavior.
|
|
36
|
+
const hasExplicitParallelGroundRail = groundFacingPins.some(({ pin: a }) => {
|
|
37
|
+
const group = physicalConnMap.getNetConnectedToId(a.pinId)
|
|
38
|
+
return (
|
|
39
|
+
group !== undefined &&
|
|
40
|
+
groundFacingPins.some(
|
|
41
|
+
({ pin: b }) =>
|
|
42
|
+
a.pinId !== b.pinId &&
|
|
43
|
+
Math.abs(a.y - b.y) < 1e-6 &&
|
|
44
|
+
Math.abs(a.x - b.x) > 1e-6 &&
|
|
45
|
+
physicalConnMap.getNetConnectedToId(b.pinId) === group,
|
|
46
|
+
)
|
|
47
|
+
)
|
|
48
|
+
})
|
|
49
|
+
if (!hasExplicitParallelGroundRail) return () => true
|
|
50
|
+
const isGroundFacingTerminal = (pinId: PinId) => {
|
|
51
|
+
const entry = pins.get(pinId)
|
|
52
|
+
return (
|
|
53
|
+
entry?.chip.pins.length === 2 &&
|
|
54
|
+
!physicalConnMap.getNetConnectedToId(pinId) &&
|
|
55
|
+
(entry.pin._facingDirection ?? getPinDirection(entry.pin, entry.chip)) ===
|
|
56
|
+
"y-"
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (firstPinId: PinId, secondPinId: PinId): boolean => {
|
|
61
|
+
if (
|
|
62
|
+
!groundNetId ||
|
|
63
|
+
netConnMap.getNetConnectedToId(firstPinId) !== groundNetId ||
|
|
64
|
+
netConnMap.getNetConnectedToId(secondPinId) !== groundNetId
|
|
65
|
+
) {
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
if (
|
|
69
|
+
!isGroundFacingTerminal(firstPinId) &&
|
|
70
|
+
!isGroundFacingTerminal(secondPinId)
|
|
71
|
+
)
|
|
72
|
+
return true
|
|
73
|
+
const first = pins.get(firstPinId)?.pin
|
|
74
|
+
const second = pins.get(secondPinId)?.pin
|
|
75
|
+
// Level ground pins can still form a shared decoupling rail. A staggered
|
|
76
|
+
// terminal would need a return detour; use its local GND label instead.
|
|
77
|
+
return (
|
|
78
|
+
!first ||
|
|
79
|
+
!second ||
|
|
80
|
+
Math.abs(first.y - second.y) <= MAX_LOCAL_GROUND_BRANCH_OFFSET + 1e-6
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -6,6 +6,7 @@ import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/
|
|
|
6
6
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
7
7
|
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
8
8
|
import { alignSameNetJunctions } from "./alignSameNetJunctions"
|
|
9
|
+
import { placeGroundRailLabelsAtOuterEnd } from "./placeGroundRailLabelsAtOuterEnd"
|
|
9
10
|
|
|
10
11
|
interface SameNetJunctionAlignmentSolverInput {
|
|
11
12
|
inputProblem: InputProblem
|
|
@@ -29,7 +30,11 @@ export class SameNetJunctionAlignmentSolver extends BaseSolver {
|
|
|
29
30
|
override _step() {
|
|
30
31
|
const result = alignSameNetJunctions(this.input)
|
|
31
32
|
this.outputTraces = result.traces
|
|
32
|
-
this.outputNetLabelPlacements =
|
|
33
|
+
this.outputNetLabelPlacements = placeGroundRailLabelsAtOuterEnd({
|
|
34
|
+
inputProblem: this.input.inputProblem,
|
|
35
|
+
traces: result.traces,
|
|
36
|
+
netLabelPlacements: result.netLabelPlacements,
|
|
37
|
+
})
|
|
33
38
|
this.stats.alignedJunctionCount = result.alignedJunctionCount
|
|
34
39
|
this.solved = true
|
|
35
40
|
}
|