@tscircuit/schematic-trace-solver 0.0.179 → 0.0.180
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 +2 -2
- package/dist/index.js +64 -2
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +63 -4
- package/lib/solvers/AvailableNetOrientationSolver/traces.ts +36 -0
- package/lib/solvers/AvailableNetOrientationSolver/types.ts +1 -0
- package/package.json +1 -1
- package/tests/repros/__snapshots__/repro-hub-usb-sheet.snap.svg +545 -0
- package/tests/repros/__snapshots__/repro-smartwatch-power-sheet.snap.svg +4 -4
- package/tests/repros/assets/repro-hub-usb-sheet.input.json +3967 -0
- package/tests/repros/repro-battery-management-bq24073.test.ts +17 -0
- package/tests/repros/repro-hub-usb-sheet.test.ts +56 -0
- package/tests/repros/repro-smartwatch-power-sheet.test.ts +31 -5
package/dist/index.d.ts
CHANGED
|
@@ -831,7 +831,7 @@ type CandidateLabel = {
|
|
|
831
831
|
height: number;
|
|
832
832
|
};
|
|
833
833
|
type CandidateStatus$2 = "valid" | "chip-collision" | "text-collision" | "trace-collision" | "trace-clearance-violation" | "netlabel-collision";
|
|
834
|
-
type CandidatePhase = "rotate" | "trace-anchor" | "connected-rail-shift" | "outward-trace-anchor" | "shift" | "lateral-shift";
|
|
834
|
+
type CandidatePhase = "rotate" | "trace-anchor" | "connected-rail-shift" | "downward-ground-rail-extension" | "outward-trace-anchor" | "shift" | "lateral-shift";
|
|
835
835
|
type EvaluatedCandidate = CandidateLabel & {
|
|
836
836
|
status: CandidateStatus$2;
|
|
837
837
|
selected: boolean;
|
|
@@ -916,7 +916,7 @@ declare class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
916
916
|
private createCandidate;
|
|
917
917
|
private getNetLabelHeight;
|
|
918
918
|
private getCandidateStatus;
|
|
919
|
-
private
|
|
919
|
+
private isAcceptableConnectedChipBoundaryOverlap;
|
|
920
920
|
private staysOutsideConnectedPinRow;
|
|
921
921
|
private getBoundsStatus;
|
|
922
922
|
private isTraceTooCloseToLabel;
|
package/dist/index.js
CHANGED
|
@@ -7709,6 +7709,27 @@ var TraceCleanupSolver = class extends BaseSolver {
|
|
|
7709
7709
|
};
|
|
7710
7710
|
|
|
7711
7711
|
// lib/solvers/AvailableNetOrientationSolver/traces.ts
|
|
7712
|
+
var extendVerticalTracePathAtInteriorPoint = ({
|
|
7713
|
+
tracePath,
|
|
7714
|
+
sourcePoint,
|
|
7715
|
+
extensionEndPoint
|
|
7716
|
+
}) => {
|
|
7717
|
+
const sourceIndex = tracePath.findIndex(
|
|
7718
|
+
(point) => pointsEqual(point, sourcePoint)
|
|
7719
|
+
);
|
|
7720
|
+
if (sourceIndex <= 0 || sourceIndex >= tracePath.length - 1) return null;
|
|
7721
|
+
const extensionDirectionY = extensionEndPoint.y - sourcePoint.y;
|
|
7722
|
+
const adjacentIndex = [sourceIndex - 1, sourceIndex + 1].find((index) => {
|
|
7723
|
+
const point = tracePath[index];
|
|
7724
|
+
return isVertical2(point, sourcePoint) && (point.y - sourcePoint.y) * extensionDirectionY < 0;
|
|
7725
|
+
});
|
|
7726
|
+
if (adjacentIndex === void 0) return null;
|
|
7727
|
+
return tracePath.toSpliced(
|
|
7728
|
+
Math.max(sourceIndex, adjacentIndex),
|
|
7729
|
+
0,
|
|
7730
|
+
extensionEndPoint
|
|
7731
|
+
);
|
|
7732
|
+
};
|
|
7712
7733
|
var getPinMap = (inputProblem) => {
|
|
7713
7734
|
const pinMap = {};
|
|
7714
7735
|
for (const chip of inputProblem.chips) {
|
|
@@ -9028,6 +9049,7 @@ function getPaddedBounds(bounds, padding) {
|
|
|
9028
9049
|
|
|
9029
9050
|
// lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts
|
|
9030
9051
|
var LABEL_TRACE_CLEARANCE2 = 0.1;
|
|
9052
|
+
var MIN_DOWNWARD_GROUND_RAIL_EXTENSION = 0.2;
|
|
9031
9053
|
var CONNECTED_PIN_ROW_OVERLAP_TOLERANCE = 0.1;
|
|
9032
9054
|
var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
9033
9055
|
inputProblem;
|
|
@@ -9267,6 +9289,20 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9267
9289
|
}
|
|
9268
9290
|
addConnectorTrace(label, candidate, labelIndex) {
|
|
9269
9291
|
if (candidate.phase === "trace-anchor") return;
|
|
9292
|
+
if (candidate.phase === "downward-ground-rail-extension") {
|
|
9293
|
+
for (const mspPairId2 of label.mspConnectionPairIds) {
|
|
9294
|
+
const hostTrace = this.traceMap[mspPairId2];
|
|
9295
|
+
if (!hostTrace) continue;
|
|
9296
|
+
const extendedTracePath = extendVerticalTracePathAtInteriorPoint({
|
|
9297
|
+
tracePath: hostTrace.tracePath,
|
|
9298
|
+
sourcePoint: label.anchorPoint,
|
|
9299
|
+
extensionEndPoint: candidate.anchorPoint
|
|
9300
|
+
});
|
|
9301
|
+
if (!extendedTracePath) continue;
|
|
9302
|
+
hostTrace.tracePath = extendedTracePath;
|
|
9303
|
+
return;
|
|
9304
|
+
}
|
|
9305
|
+
}
|
|
9270
9306
|
const tracePath = this.getCandidateConnectorTrace(
|
|
9271
9307
|
label,
|
|
9272
9308
|
candidate,
|
|
@@ -9313,6 +9349,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9313
9349
|
(connection) => connection.netId === label.netId
|
|
9314
9350
|
);
|
|
9315
9351
|
const isTwoPinNet = netConnection?.pinIds.length === 2;
|
|
9352
|
+
const isDownwardGroundRail = netConnection?.isGround && isXOrientation(label.orientation) && orientations.length === 1 && requiredOrientation === "y-" && this.hasTraceContinuingInOrientation(label, "y+");
|
|
9316
9353
|
const isDistanceSplitVerticalRail = (netConnection?.pinIds.length ?? 0) > 2 && isYOrientation(requiredOrientation) && this.hasPortOnlyLabelOnSameNet(label);
|
|
9317
9354
|
const textBlockedMultiPinRailTraces = this.getTextBlockedMultiPinRailTraces(
|
|
9318
9355
|
label,
|
|
@@ -9370,6 +9407,26 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
9370
9407
|
labelIndex,
|
|
9371
9408
|
orientations
|
|
9372
9409
|
);
|
|
9410
|
+
const rotatedCandidateNeedsLateralBranch = rotatedCandidate !== null && Math.abs(rotatedCandidate.anchorPoint.x - label.anchorPoint.x) > EPS13;
|
|
9411
|
+
if (isDownwardGroundRail && rotatedCandidateNeedsLateralBranch) {
|
|
9412
|
+
const downwardRailCandidate = this.findValidCandidateInShiftColumn({
|
|
9413
|
+
label,
|
|
9414
|
+
labelIndex,
|
|
9415
|
+
orientation: requiredOrientation,
|
|
9416
|
+
direction: dir(requiredOrientation),
|
|
9417
|
+
baseAnchor: label.anchorPoint,
|
|
9418
|
+
maxSearchDistance: this.getSearchDistanceLimit(
|
|
9419
|
+
label,
|
|
9420
|
+
requiredOrientation
|
|
9421
|
+
),
|
|
9422
|
+
outwardDistance: 0,
|
|
9423
|
+
phase: "downward-ground-rail-extension",
|
|
9424
|
+
stopOnTraceCollision: false,
|
|
9425
|
+
connectorSource: label.anchorPoint,
|
|
9426
|
+
startDistance: MIN_DOWNWARD_GROUND_RAIL_EXTENSION
|
|
9427
|
+
});
|
|
9428
|
+
if (downwardRailCandidate) return downwardRailCandidate;
|
|
9429
|
+
}
|
|
9373
9430
|
if (rotatedCandidate) return rotatedCandidate;
|
|
9374
9431
|
const traceAnchorCandidate = this.findValidTraceAnchorCandidate(
|
|
9375
9432
|
label,
|
|
@@ -10096,7 +10153,12 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
10096
10153
|
label
|
|
10097
10154
|
});
|
|
10098
10155
|
if (boundsStatus !== "valid") {
|
|
10099
|
-
|
|
10156
|
+
const canOverlapConnectedChipBoundary = phase === "trace-anchor" || phase === "connected-rail-shift" || phase === "downward-ground-rail-extension";
|
|
10157
|
+
if (!canOverlapConnectedChipBoundary || boundsStatus !== "chip-collision" || !this.isAcceptableConnectedChipBoundaryOverlap(
|
|
10158
|
+
candidate,
|
|
10159
|
+
label,
|
|
10160
|
+
bounds
|
|
10161
|
+
) || phase === "connected-rail-shift" && !this.staysOutsideConnectedPinRow({ candidate, label, bounds })) {
|
|
10100
10162
|
return boundsStatus;
|
|
10101
10163
|
}
|
|
10102
10164
|
const nonChipBoundsStatus = this.getBoundsStatus({
|
|
@@ -10140,7 +10202,7 @@ var AvailableNetOrientationSolver = class extends BaseSolver {
|
|
|
10140
10202
|
}
|
|
10141
10203
|
return "valid";
|
|
10142
10204
|
}
|
|
10143
|
-
|
|
10205
|
+
isAcceptableConnectedChipBoundaryOverlap(candidate, label, bounds) {
|
|
10144
10206
|
const collidingChips = this.chipObstacleSpatialIndex.getChipsInBounds(bounds);
|
|
10145
10207
|
if (collidingChips.length === 0) return false;
|
|
10146
10208
|
const labelChipIds = new Set(
|
|
@@ -41,7 +41,12 @@ import {
|
|
|
41
41
|
tracePathIntersectsBounds,
|
|
42
42
|
} from "./geometry"
|
|
43
43
|
import { orderRoutedLabelsBeforeOverlappingPortLabels } from "./orderRoutedLabelsBeforeOverlappingPortLabels"
|
|
44
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
extendVerticalTracePathAtInteriorPoint,
|
|
46
|
+
getPinMap,
|
|
47
|
+
getTracePins,
|
|
48
|
+
toNetLabelPlacementPatch,
|
|
49
|
+
} from "./traces"
|
|
45
50
|
import type {
|
|
46
51
|
AvailableNetOrientationSolverParams,
|
|
47
52
|
Bounds,
|
|
@@ -55,6 +60,7 @@ import { visualizeAvailableNetOrientationSolver } from "./visualize"
|
|
|
55
60
|
import { AvailableNetOrientationObstacleIndex } from "./AvailableNetOrientationObstacleIndex"
|
|
56
61
|
|
|
57
62
|
const LABEL_TRACE_CLEARANCE = 0.1
|
|
63
|
+
const MIN_DOWNWARD_GROUND_RAIL_EXTENSION = 0.2
|
|
58
64
|
const CONNECTED_PIN_ROW_OVERLAP_TOLERANCE = 0.1
|
|
59
65
|
|
|
60
66
|
export class AvailableNetOrientationSolver extends BaseSolver {
|
|
@@ -357,6 +363,21 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
357
363
|
) {
|
|
358
364
|
if (candidate.phase === "trace-anchor") return
|
|
359
365
|
|
|
366
|
+
if (candidate.phase === "downward-ground-rail-extension") {
|
|
367
|
+
for (const mspPairId of label.mspConnectionPairIds) {
|
|
368
|
+
const hostTrace = this.traceMap[mspPairId]
|
|
369
|
+
if (!hostTrace) continue
|
|
370
|
+
const extendedTracePath = extendVerticalTracePathAtInteriorPoint({
|
|
371
|
+
tracePath: hostTrace.tracePath,
|
|
372
|
+
sourcePoint: label.anchorPoint,
|
|
373
|
+
extensionEndPoint: candidate.anchorPoint,
|
|
374
|
+
})
|
|
375
|
+
if (!extendedTracePath) continue
|
|
376
|
+
hostTrace.tracePath = extendedTracePath
|
|
377
|
+
return
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
360
381
|
const tracePath = this.getCandidateConnectorTrace(
|
|
361
382
|
label,
|
|
362
383
|
candidate,
|
|
@@ -416,6 +437,12 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
416
437
|
(connection) => connection.netId === label.netId,
|
|
417
438
|
)
|
|
418
439
|
const isTwoPinNet = netConnection?.pinIds.length === 2
|
|
440
|
+
const isDownwardGroundRail =
|
|
441
|
+
netConnection?.isGround &&
|
|
442
|
+
isXOrientation(label.orientation) &&
|
|
443
|
+
orientations.length === 1 &&
|
|
444
|
+
requiredOrientation === "y-" &&
|
|
445
|
+
this.hasTraceContinuingInOrientation(label, "y+")
|
|
419
446
|
const isDistanceSplitVerticalRail =
|
|
420
447
|
(netConnection?.pinIds.length ?? 0) > 2 &&
|
|
421
448
|
isYOrientation(requiredOrientation) &&
|
|
@@ -495,6 +522,30 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
495
522
|
labelIndex,
|
|
496
523
|
orientations,
|
|
497
524
|
)
|
|
525
|
+
const rotatedCandidateNeedsLateralBranch =
|
|
526
|
+
rotatedCandidate !== null &&
|
|
527
|
+
Math.abs(rotatedCandidate.anchorPoint.x - label.anchorPoint.x) > EPS
|
|
528
|
+
|
|
529
|
+
if (isDownwardGroundRail && rotatedCandidateNeedsLateralBranch) {
|
|
530
|
+
const downwardRailCandidate = this.findValidCandidateInShiftColumn({
|
|
531
|
+
label,
|
|
532
|
+
labelIndex,
|
|
533
|
+
orientation: requiredOrientation,
|
|
534
|
+
direction: dir(requiredOrientation),
|
|
535
|
+
baseAnchor: label.anchorPoint,
|
|
536
|
+
maxSearchDistance: this.getSearchDistanceLimit(
|
|
537
|
+
label,
|
|
538
|
+
requiredOrientation,
|
|
539
|
+
),
|
|
540
|
+
outwardDistance: 0,
|
|
541
|
+
phase: "downward-ground-rail-extension",
|
|
542
|
+
stopOnTraceCollision: false,
|
|
543
|
+
connectorSource: label.anchorPoint,
|
|
544
|
+
startDistance: MIN_DOWNWARD_GROUND_RAIL_EXTENSION,
|
|
545
|
+
})
|
|
546
|
+
if (downwardRailCandidate) return downwardRailCandidate
|
|
547
|
+
}
|
|
548
|
+
|
|
498
549
|
if (rotatedCandidate) return rotatedCandidate
|
|
499
550
|
|
|
500
551
|
const traceAnchorCandidate = this.findValidTraceAnchorCandidate(
|
|
@@ -1525,10 +1576,18 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1525
1576
|
label,
|
|
1526
1577
|
})
|
|
1527
1578
|
if (boundsStatus !== "valid") {
|
|
1579
|
+
const canOverlapConnectedChipBoundary =
|
|
1580
|
+
phase === "trace-anchor" ||
|
|
1581
|
+
phase === "connected-rail-shift" ||
|
|
1582
|
+
phase === "downward-ground-rail-extension"
|
|
1528
1583
|
if (
|
|
1529
|
-
|
|
1584
|
+
!canOverlapConnectedChipBoundary ||
|
|
1530
1585
|
boundsStatus !== "chip-collision" ||
|
|
1531
|
-
!this.
|
|
1586
|
+
!this.isAcceptableConnectedChipBoundaryOverlap(
|
|
1587
|
+
candidate,
|
|
1588
|
+
label,
|
|
1589
|
+
bounds,
|
|
1590
|
+
) ||
|
|
1532
1591
|
(phase === "connected-rail-shift" &&
|
|
1533
1592
|
!this.staysOutsideConnectedPinRow({ candidate, label, bounds }))
|
|
1534
1593
|
) {
|
|
@@ -1587,7 +1646,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
1587
1646
|
return "valid"
|
|
1588
1647
|
}
|
|
1589
1648
|
|
|
1590
|
-
private
|
|
1649
|
+
private isAcceptableConnectedChipBoundaryOverlap(
|
|
1591
1650
|
candidate: CandidateLabel,
|
|
1592
1651
|
label: NetLabelPlacement,
|
|
1593
1652
|
bounds: Bounds,
|
|
@@ -1,8 +1,44 @@
|
|
|
1
|
+
import type { Point } from "@tscircuit/math-utils"
|
|
1
2
|
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
2
3
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
|
+
import {
|
|
5
|
+
isVertical,
|
|
6
|
+
pointsEqual,
|
|
7
|
+
} from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/geometry"
|
|
3
8
|
import type { InputPin, InputProblem } from "lib/types/InputProblem"
|
|
4
9
|
import type { CandidateLabel } from "./types"
|
|
5
10
|
|
|
11
|
+
export const extendVerticalTracePathAtInteriorPoint = ({
|
|
12
|
+
tracePath,
|
|
13
|
+
sourcePoint,
|
|
14
|
+
extensionEndPoint,
|
|
15
|
+
}: {
|
|
16
|
+
tracePath: Point[]
|
|
17
|
+
sourcePoint: Point
|
|
18
|
+
extensionEndPoint: Point
|
|
19
|
+
}) => {
|
|
20
|
+
const sourceIndex = tracePath.findIndex((point) =>
|
|
21
|
+
pointsEqual(point, sourcePoint),
|
|
22
|
+
)
|
|
23
|
+
if (sourceIndex <= 0 || sourceIndex >= tracePath.length - 1) return null
|
|
24
|
+
|
|
25
|
+
const extensionDirectionY = extensionEndPoint.y - sourcePoint.y
|
|
26
|
+
const adjacentIndex = [sourceIndex - 1, sourceIndex + 1].find((index) => {
|
|
27
|
+
const point = tracePath[index]!
|
|
28
|
+
return (
|
|
29
|
+
isVertical(point, sourcePoint) &&
|
|
30
|
+
(point.y - sourcePoint.y) * extensionDirectionY < 0
|
|
31
|
+
)
|
|
32
|
+
})
|
|
33
|
+
if (adjacentIndex === undefined) return null
|
|
34
|
+
|
|
35
|
+
return tracePath.toSpliced(
|
|
36
|
+
Math.max(sourceIndex, adjacentIndex),
|
|
37
|
+
0,
|
|
38
|
+
extensionEndPoint,
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
6
42
|
export const getPinMap = (inputProblem: InputProblem) => {
|
|
7
43
|
const pinMap: Record<string, InputPin & { chipId: string }> = {}
|
|
8
44
|
for (const chip of inputProblem.chips) {
|