@tscircuit/schematic-trace-solver 0.0.125 → 0.0.127

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/README.md +28 -0
  2. package/dist/index.d.ts +159 -2
  3. package/dist/index.js +739 -36
  4. package/lib/index.ts +2 -0
  5. package/lib/solvers/InlineNetLabelSolver/InlineNetLabelSolver.ts +424 -0
  6. package/lib/solvers/InlineNetLabelSolver/getAxisAlignedSegments.ts +73 -0
  7. package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +15 -0
  8. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +86 -11
  9. package/lib/solvers/TraceElbowTransitionSimplificationSolver/TraceElbowTransitionSimplificationSolver.ts +216 -0
  10. package/lib/solvers/TraceElbowTransitionSimplificationSolver/generateElbowTransitionSimplificationCandidates.ts +186 -0
  11. package/lib/solvers/TraceElbowTransitionSimplificationSolver/types.ts +10 -0
  12. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +3 -0
  13. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +16 -0
  14. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +56 -13
  15. package/lib/types/InputProblem.ts +24 -0
  16. package/package.json +1 -1
  17. package/site/bug-reports/bug-report-20260806T093501Z.page.tsx +4 -0
  18. package/site/examples/inline-net-label01.page.tsx +6 -0
  19. package/tests/assets/inline-net-label01.json +47 -0
  20. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +3 -3
  21. package/tests/bug-reports/bug-report-20260706T220324Z/__snapshots__/bug-report-20260706T220324Z.snap.svg +6 -6
  22. package/tests/bug-reports/bug-report-20260806T093501Z/__snapshots__/bug-report-20260806T093501Z.snap.svg +135 -0
  23. package/tests/bug-reports/bug-report-20260806T093501Z/bug-report-20260806T093501Z.json +505 -0
  24. package/tests/bug-reports/bug-report-20260806T093501Z/bug-report-20260806T093501Z.test.ts +66 -0
  25. package/tests/examples/__snapshots__/example33.snap.svg +1 -1
  26. package/tests/functions/getAxisAlignedSegments.test.ts +56 -0
  27. package/tests/solvers/InlineNetLabelSolver/__snapshots__/inline-net-label01.snap.svg +54 -0
  28. package/tests/solvers/InlineNetLabelSolver/inline-net-label01.test.ts +44 -0
package/README.md CHANGED
@@ -35,6 +35,34 @@ Finally, the `NetLabelPlacementSolver` places net labels for each net connection
35
35
  this requires drawing small traces to adapt to the `availableFacingDirections` of the net connection.
36
36
  If there is crowding at the pin, we look for an available spot along the trace connected to the pin.
37
37
 
38
+ ### Inline net labels
39
+
40
+ A point-to-point signal trace can be labeled *inline*: the net name is drawn
41
+ parallel to the wire (above a horizontal trace, to the left of a vertical one)
42
+ instead of as an anchored label hanging off the end of it. This keeps the visual
43
+ line between the two pins intact.
44
+
45
+ Inline labels are opt-in per direct connection - set `allowInlineNetLabel: true`
46
+ on the `directConnection`. The caller (usually [@tscircuit/core](https://github.com/tscircuit/core))
47
+ decides which connections deserve one; the solver only honors the request when
48
+ the connection actually got routed, since there is nothing to run parallel to
49
+ otherwise.
50
+
51
+ The `InlineNetLabelSolver` runs last in the pipeline and exposes
52
+ `inlineNetLabelPlacements`. A net that receives an inline label has its anchored
53
+ `NetLabelPlacement` removed from `getOutput().netLabelPlacements`, so a net is
54
+ never labeled twice.
55
+
56
+ ```tsx
57
+ directConnections: [
58
+ {
59
+ pinIds: ["U1.1", "D1.1"],
60
+ netId: "USER_LED_ANODE",
61
+ allowInlineNetLabel: true,
62
+ },
63
+ ]
64
+ ```
65
+
38
66
  ## Usage
39
67
 
40
68
  ```tsx
package/dist/index.d.ts CHANGED
@@ -88,6 +88,27 @@ interface InputDirectConnection {
88
88
  pinIds: [PinId, PinId];
89
89
  netId?: string;
90
90
  netLabelWidth?: number;
91
+ /**
92
+ * When true, this point-to-point connection may be labeled with an "inline
93
+ * net label": the net name is drawn parallel to (and offset from) the routed
94
+ * trace instead of being placed as a separate anchored net label at the end
95
+ * of the trace.
96
+ *
97
+ * Only set this for connections whose net name is worth showing on the wire -
98
+ * the solver trusts the caller (e.g. @tscircuit/core) to make that decision.
99
+ * An inline label is only emitted when the connection actually got routed.
100
+ */
101
+ allowInlineNetLabel?: boolean;
102
+ /**
103
+ * Extent of the inline net label along the trace. Falls back to
104
+ * `netLabelWidth`, then to an estimate from the netId text.
105
+ */
106
+ inlineNetLabelWidth?: number;
107
+ /**
108
+ * Height of the inline net label text. Defaults to
109
+ * DEFAULT_INLINE_NET_LABEL_HEIGHT.
110
+ */
111
+ inlineNetLabelHeight?: number;
91
112
  }
92
113
  interface InputNetConnection {
93
114
  netId: string;
@@ -441,6 +462,13 @@ declare class NetLabelPlacementSolver extends BaseSolver {
441
462
  visualize(): GraphicsObject;
442
463
  }
443
464
 
465
+ interface CompletedTraceReroute {
466
+ initialTrace: SolvedTracePath;
467
+ reroutedTracePath: Point[];
468
+ label: NetLabelPlacement;
469
+ detourCount: number;
470
+ }
471
+
444
472
  interface LabelMergingSolverInput {
445
473
  netLabelPlacements: NetLabelPlacement[];
446
474
  inputProblem: InputProblem;
@@ -479,6 +507,7 @@ interface SingleOverlapSolverInput {
479
507
  paddingBuffer: number;
480
508
  detourCount: number;
481
509
  tracesToAvoidOverlapping?: SolvedTracePath[];
510
+ netLabelPlacements?: NetLabelPlacement[];
482
511
  }
483
512
  /**
484
513
  * This solver attempts to find a valid rerouting for a single trace that is
@@ -493,6 +522,8 @@ declare class SingleOverlapSolver extends BaseSolver {
493
522
  obstacles: ReturnType<typeof getObstacleRects>;
494
523
  label: NetLabelPlacement;
495
524
  tracesToAvoidOverlapping: SolvedTracePath[];
525
+ netLabelPlacements: NetLabelPlacement[];
526
+ detourCount: number;
496
527
  _tried: number;
497
528
  constructor(solverInput: SingleOverlapSolverInput);
498
529
  _step(): void;
@@ -520,6 +551,7 @@ declare class OverlapAvoidanceStepSolver extends BaseSolver {
520
551
  allTraces: SolvedTracePath[];
521
552
  tracesToAvoidOverlapping: SolvedTracePath[];
522
553
  modifiedTraces: SolvedTracePath[];
554
+ completedReroutes: CompletedTraceReroute[];
523
555
  private readonly PADDING_BUFFER;
524
556
  private detourCounts;
525
557
  activeSubSolver: SingleOverlapSolver | null;
@@ -570,6 +602,7 @@ declare class TraceLabelOverlapAvoidanceSolver extends BaseSolver {
570
602
  _step(): void;
571
603
  getOutput(): {
572
604
  traces: SolvedTracePath[];
605
+ completedReroutes: CompletedTraceReroute[];
573
606
  netLabelPlacements: NetLabelPlacement[];
574
607
  };
575
608
  visualize(): GraphicsObject;
@@ -958,6 +991,7 @@ declare class NetLabelTraceCollisionSolver extends BaseSolver {
958
991
  netLabelPlacements: NetLabelPlacement[];
959
992
  outputTraces: SolvedTracePath[];
960
993
  outputNetLabelPlacements: NetLabelPlacement[];
994
+ completedReroutes: CompletedTraceReroute[];
961
995
  activeSubSolver: SingleOverlapSolver | null;
962
996
  private recentlyFailed;
963
997
  private detourCounts;
@@ -972,6 +1006,7 @@ declare class NetLabelTraceCollisionSolver extends BaseSolver {
972
1006
  private reanchorLabelsOnMovedTrace;
973
1007
  getOutput(): {
974
1008
  traces: SolvedTracePath[];
1009
+ completedReroutes: CompletedTraceReroute[];
975
1010
  netLabelPlacements: NetLabelPlacement[];
976
1011
  };
977
1012
  private getOverlapKey;
@@ -1083,6 +1118,124 @@ declare class SameNetJunctionAlignmentSolver extends BaseSolver {
1083
1118
  visualize(): GraphicsObject;
1084
1119
  }
1085
1120
 
1121
+ interface TraceElbowTransitionSimplificationSolverInput {
1122
+ inputProblem: InputProblem;
1123
+ traces: SolvedTracePath[];
1124
+ completedReroutes: CompletedTraceReroute[];
1125
+ netLabelPlacements: NetLabelPlacement[];
1126
+ paddingBuffer: number;
1127
+ }
1128
+ /**
1129
+ * Post-processes traces after trace/label overlap avoidance. It removes
1130
+ * redundant elbow transitions and, when needed, shifts a simplified elbow
1131
+ * around a rendered label. Every replacement is revalidated against labels,
1132
+ * components, other-net traces, and label anchors.
1133
+ */
1134
+ declare class TraceElbowTransitionSimplificationSolver extends BaseSolver {
1135
+ private input;
1136
+ private outputTraces;
1137
+ private traceIdQueue;
1138
+ private obstacles;
1139
+ constructor(input: TraceElbowTransitionSimplificationSolverInput);
1140
+ _step(): void;
1141
+ getOutput(): {
1142
+ traces: SolvedTracePath[];
1143
+ netLabelPlacements: NetLabelPlacement[];
1144
+ };
1145
+ visualize(): GraphicsObject;
1146
+ }
1147
+
1148
+ declare const DEFAULT_INLINE_NET_LABEL_HEIGHT = 0.18;
1149
+ /**
1150
+ * Gap between the trace and the near edge of the inline label text.
1151
+ */
1152
+ declare const INLINE_NET_LABEL_TRACE_MARGIN = 0.05;
1153
+ /**
1154
+ * How much of the label is allowed to hang off the end of the wire it names,
1155
+ * as a fraction of the label's length. A short elbow stub is technically clear
1156
+ * of every obstacle but reads as a label floating in space, so runs shorter
1157
+ * than this are not considered.
1158
+ */
1159
+ declare const MIN_INLINE_NET_LABEL_SEGMENT_RATIO = 0.5;
1160
+ /**
1161
+ * A net label drawn parallel to the trace it names, rather than anchored to the
1162
+ * end of it. Used for point-to-point signal traces, where an anchored label
1163
+ * would break the visual line between the two pins.
1164
+ *
1165
+ * `center` is the center of the text box in the schematic's own (unrotated)
1166
+ * coordinate space. `width` is the extent of the text along the trace and
1167
+ * `height` is its extent perpendicular to the trace, so a vertical label has
1168
+ * its `width` running along y.
1169
+ */
1170
+ interface InlineNetLabelPlacement {
1171
+ globalConnNetId: string;
1172
+ netId?: string;
1173
+ mspPairId: string;
1174
+ pinIds: PinId[];
1175
+ /** Axis the text runs along: "x" reads left-to-right, "y" reads bottom-to-top */
1176
+ axis: "x" | "y";
1177
+ /** Midpoint of the trace segment the label is attached to */
1178
+ anchorPoint: Point;
1179
+ /** Center of the label text, offset perpendicular to the trace */
1180
+ center: Point;
1181
+ /** Extent along the trace */
1182
+ width: number;
1183
+ /** Extent perpendicular to the trace */
1184
+ height: number;
1185
+ /**
1186
+ * Which side of the trace the label sits on. For a horizontal trace, "y+" is
1187
+ * above; for a vertical trace, "x-" is the left side (which is "above" once
1188
+ * the text is rotated).
1189
+ */
1190
+ side: "x+" | "x-" | "y+" | "y-";
1191
+ }
1192
+ interface InlineNetLabelSolverInput {
1193
+ inputProblem: InputProblem;
1194
+ traces: SolvedTracePath[];
1195
+ netLabelPlacements: NetLabelPlacement[];
1196
+ }
1197
+ /**
1198
+ * Places "inline net labels" - net names drawn alongside the trace they belong
1199
+ * to - for direct connections that opted in via `allowInlineNetLabel`.
1200
+ *
1201
+ * Any regular (anchored) net label placement for the same net is dropped, so a
1202
+ * net is never labeled twice.
1203
+ */
1204
+ declare class InlineNetLabelSolver extends BaseSolver {
1205
+ inputProblem: InputProblem;
1206
+ traces: SolvedTracePath[];
1207
+ inputNetLabelPlacements: NetLabelPlacement[];
1208
+ inlineNetLabelPlacements: InlineNetLabelPlacement[];
1209
+ /** Direct connections that opted in, still waiting to be processed */
1210
+ queuedDirectConnections: InputDirectConnection[];
1211
+ private tracesByPinPairKey;
1212
+ constructor(input: InlineNetLabelSolverInput);
1213
+ getConstructorParams(): [InlineNetLabelSolverInput];
1214
+ _step(): void;
1215
+ private computeInlinePlacement;
1216
+ /**
1217
+ * An inline label may not sit on top of a chip, a component's text, or a
1218
+ * trace belonging to another net.
1219
+ */
1220
+ private isObstructed;
1221
+ /**
1222
+ * Net label placements superseded by an inline label. A net gets one label or
1223
+ * the other, never both.
1224
+ */
1225
+ private getSupersededNetLabelKeys;
1226
+ getOutput(): {
1227
+ traces: SolvedTracePath[];
1228
+ netLabelPlacements: NetLabelPlacement[];
1229
+ inlineNetLabelPlacements: InlineNetLabelPlacement[];
1230
+ };
1231
+ visualize(): GraphicsObject;
1232
+ }
1233
+ /**
1234
+ * Mirrors the net label text width used by @tscircuit/core so a label that core
1235
+ * did not measure for us still reserves a sane amount of space.
1236
+ */
1237
+ declare const estimateInlineNetLabelWidth: (text: string, fontSize?: number) => number;
1238
+
1086
1239
  /**
1087
1240
  * Pipeline solver that runs a series of solvers to find the best schematic layout.
1088
1241
  * Coordinates the entire layout process from chip partitioning through final packing.
@@ -1117,14 +1270,18 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
1117
1270
  netLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver;
1118
1271
  traceCleanupSolver2?: TraceCleanupSolver;
1119
1272
  netLabelNetLabelCollisionSolver?: NetLabelNetLabelCollisionSolver;
1273
+ traceElbowTransitionSimplificationSolver?: TraceElbowTransitionSimplificationSolver;
1274
+ preAlignmentTraceElbowTransitionSimplificationSolver?: TraceElbowTransitionSimplificationSolver;
1275
+ finalTraceElbowTransitionSimplificationSolver?: TraceElbowTransitionSimplificationSolver;
1120
1276
  sameNetJunctionAlignmentSolver?: SameNetJunctionAlignmentSolver;
1277
+ inlineNetLabelSolver?: InlineNetLabelSolver;
1121
1278
  startTimeOfPhase: Record<string, number>;
1122
1279
  endTimeOfPhase: Record<string, number>;
1123
1280
  timeSpentOnPhase: Record<string, number>;
1124
1281
  firstIterationOfPhase: Record<string, number>;
1125
1282
  inputProblem: InputProblem;
1126
1283
  hideRatsNet: boolean;
1127
- pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof UnroutedTraceRecoverySolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver> | PipelineStep<typeof TraceCleanupSolver> | PipelineStep<typeof Example28Solver> | PipelineStep<typeof AvailableNetOrientationSolver> | PipelineStep<typeof RailNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver> | PipelineStep<typeof NetLabelTraceCollisionSolver> | PipelineStep<typeof NetLabelNetLabelCollisionSolver> | PipelineStep<typeof SameNetJunctionAlignmentSolver>)[];
1284
+ pipelineDef: (PipelineStep<typeof MspConnectionPairSolver> | PipelineStep<typeof SchematicTraceLinesSolver> | PipelineStep<typeof LongDistancePairSolver> | PipelineStep<typeof UnroutedTraceRecoverySolver> | PipelineStep<typeof TraceOverlapShiftSolver> | PipelineStep<typeof NetLabelPlacementSolver> | PipelineStep<typeof TraceLabelOverlapAvoidanceSolver> | PipelineStep<typeof TraceElbowTransitionSimplificationSolver> | PipelineStep<typeof TraceCleanupSolver> | PipelineStep<typeof Example28Solver> | PipelineStep<typeof AvailableNetOrientationSolver> | PipelineStep<typeof RailNetLabelCornerPlacementSolver> | PipelineStep<typeof TraceAnchoredNetLabelOverlapSolver> | PipelineStep<typeof NetLabelTraceCollisionSolver> | PipelineStep<typeof NetLabelNetLabelCollisionSolver> | PipelineStep<typeof SameNetJunctionAlignmentSolver> | PipelineStep<typeof InlineNetLabelSolver>)[];
1128
1285
  constructor(inputProblem: InputProblem, opts?: Options);
1129
1286
  getConstructorParams(): ConstructorParameters<typeof SchematicTracePipelineSolver>;
1130
1287
  currentPipelineStepIndex: number;
@@ -1140,4 +1297,4 @@ declare class SchematicTracePipelineSolver extends BaseSolver {
1140
1297
  preview(): GraphicsObject;
1141
1298
  }
1142
1299
 
1143
- export { type ChipId, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem, type NetId, type PinId, SchematicTracePipelineSolver, SchematicTraceSingleLineSolver2, type SectionId, type TextBoxes };
1300
+ export { type ChipId, DEFAULT_INLINE_NET_LABEL_HEIGHT, INLINE_NET_LABEL_TRACE_MARGIN, type InlineNetLabelPlacement, InlineNetLabelSolver, type InputChip, type InputDirectConnection, type InputNetConnection, type InputPin, type InputProblem, MIN_INLINE_NET_LABEL_SEGMENT_RATIO, type NetId, type NetLabelPlacement, type PinId, SchematicTracePipelineSolver, SchematicTraceSingleLineSolver2, type SectionId, type TextBoxes, estimateInlineNetLabelWidth };