@tscircuit/schematic-trace-solver 0.0.203 → 0.0.204

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 (30) hide show
  1. package/dist/index.js +18 -6
  2. package/lib/solvers/TraceCleanupSolver/sameNetRailAlignment/getFixedLabelCoordinate.ts +17 -8
  3. package/lib/solvers/TraceElbowTransitionSimplificationSolver/TraceElbowTransitionSimplificationSolver.ts +25 -3
  4. package/package.json +1 -1
  5. package/site/SchematicTracePipelineSolver/repro-smart-lock-ble-module.page.tsx +7 -0
  6. package/tests/bug-reports/bug-report-20260706T213649Z/__snapshots__/bug-report-20260706T213649Z.snap.svg +78 -60
  7. package/tests/bug-reports/bug-report-20260707T134722Z/__snapshots__/bug-report-20260707T134722Z.snap.svg +62 -62
  8. package/tests/bug-reports/bug-report-20260708T055430Z/__snapshots__/bug-report-20260708T055430Z.snap.svg +2 -2
  9. package/tests/bug-reports/bug-report-20260721T221026Z/__snapshots__/bug-report-20260721T221026Z.snap.svg +2 -2
  10. package/tests/bug-reports/bug-report-20260728T144234Z/__snapshots__/bug-report-20260728T144234Z.snap.svg +2 -2
  11. package/tests/bug-reports/bug-report-20260901T055358Z/__snapshots__/bug-report-20260901T055358Z.snap.svg +10 -10
  12. package/tests/bug-reports/bug-report-20260905T041712Z/__snapshots__/bug-report-20260905T041712Z.snap.svg +10 -10
  13. package/tests/bug-reports/bug-report-20260907T110144Z/__snapshots__/bug-report-20260907T110144Z.snap.svg +9 -9
  14. package/tests/bug-reports/bug-report-20260907T145640Z/__snapshots__/bug-report-20260907T145640Z.snap.svg +8 -8
  15. package/tests/bug-reports/bug-report-20260911T195723Z/__snapshots__/bug-report-20260911T195723Z.snap.svg +19 -19
  16. package/tests/bug-reports/bug-report-20260916T054012Z/__snapshots__/bug-report-20260916T054012Z.snap.svg +292 -292
  17. package/tests/examples/__snapshots__/example30.snap.svg +123 -123
  18. package/tests/repros/__snapshots__/board-1273-trace-overlap-cycle.snap.svg +18 -18
  19. package/tests/repros/__snapshots__/board-15984-same-net-junction.snap.svg +2 -2
  20. package/tests/repros/__snapshots__/repro-am3352-sheet3-power-rails.snap.svg +2 -2
  21. package/tests/repros/__snapshots__/repro-hub-usb-sheet.snap.svg +2 -2
  22. package/tests/repros/__snapshots__/repro-ina237-current-monitor.snap.svg +2 -2
  23. package/tests/repros/__snapshots__/repro-pmp11282-isolated-dcdc.snap.svg +2 -2
  24. package/tests/repros/__snapshots__/repro-robot-controller-imu-tof.snap.svg +7 -7
  25. package/tests/repros/__snapshots__/repro-smart-lock-ble-module.snap.svg +465 -0
  26. package/tests/repros/__snapshots__/repro-tida010076-page02.snap.svg +10 -10
  27. package/tests/repros/assets/repro-smart-lock-ble-module.input.json +2183 -0
  28. package/tests/repros/repro-smart-lock-ble-module.test.ts +69 -0
  29. package/tests/solvers/TraceCleanupSolver/alignSameNetRails-fixed-backbone.test.ts +135 -0
  30. package/tests/solvers/TraceElbowTransitionSimplificationSolver/interior-staircase.test.ts +146 -0
package/dist/index.js CHANGED
@@ -6328,10 +6328,6 @@ var getFixedLabelCoordinate = (group, netLabelPlacements, traces) => {
6328
6328
  const groupIsWithinSingleComponent = [...groupTraceIds].every(
6329
6329
  (traceId) => traceMap.get(traceId)?.pins.every((pin) => pin.chipId === group[0].componentId) === true
6330
6330
  );
6331
- const groupHasOnlySingleRailTraces = [...groupTraceIds].every(
6332
- (traceId) => traceMap.get(traceId)?.tracePath.length === 4
6333
- );
6334
- if (!groupHasOnlySingleRailTraces) return null;
6335
6331
  const orientation = group[0].orientation;
6336
6332
  const linkedLabels = getLabelsLinkedToRailGroup(
6337
6333
  group,
@@ -6362,6 +6358,11 @@ var getFixedLabelCoordinate = (group, netLabelPlacements, traces) => {
6362
6358
  const coordinate = group.find(
6363
6359
  (segment) => Math.abs(segment.coordinate - labelCoordinate) <= RAIL_ALIGNMENT_EPSILON
6364
6360
  )?.coordinate ?? labelCoordinate;
6361
+ if (group.some(
6362
+ (segment) => traceMap.get(segment.traceId)?.tracePath.length !== 4 && !nearlyEqual(segment.coordinate, coordinate)
6363
+ )) {
6364
+ return null;
6365
+ }
6365
6366
  const labelIsAnchoredToRoutedBackbone = anchoringLabels.some(
6366
6367
  (label) => label.mspConnectionPairIds.some(
6367
6368
  (traceId) => (traceMap.get(traceId)?.tracePath.length ?? 0) > 4
@@ -15505,7 +15506,17 @@ var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
15505
15506
  const otherNetTraces = this.outputTraces.filter(
15506
15507
  (otherTrace) => otherTrace.mspPairId !== trace.mspPairId && otherTrace.globalConnNetId !== trace.globalConnNetId
15507
15508
  );
15509
+ const initialPathLength = getPathLength6(tracePath);
15508
15510
  const candidateByPath = /* @__PURE__ */ new Map();
15511
+ for (const candidate of getInteriorShortcutPaths(tracePath)) {
15512
+ if (Math.abs(getPathLength6(candidate) - initialPathLength) > PATH_LENGTH_EPSILON3) {
15513
+ continue;
15514
+ }
15515
+ candidateByPath.set(
15516
+ candidate.map((point) => `${point.x},${point.y}`).join(";"),
15517
+ candidate
15518
+ );
15519
+ }
15509
15520
  const completedReroutes = this.input.completedReroutes.filter(
15510
15521
  (completedReroute) => completedReroute.initialTrace.mspPairId === trace.mspPairId
15511
15522
  );
@@ -15557,7 +15568,6 @@ var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
15557
15568
  ({ label }) => `${label.globalConnNetId}:${label.netId}`
15558
15569
  )
15559
15570
  );
15560
- const initialPathLength = getPathLength6(tracePath);
15561
15571
  const validCandidates = [...candidateByPath.values()].filter(
15562
15572
  (candidatePath) => {
15563
15573
  const candidateTrace = { ...trace, tracePath: candidatePath };
@@ -15574,7 +15584,9 @@ var TraceElbowTransitionSimplificationSolver = class extends BaseSolver {
15574
15584
  this.input.netLabelPlacements,
15575
15585
  [trace],
15576
15586
  [candidateTrace]
15577
- ) && !isPathCollidingWithObstacles(candidatePath, this.obstacles) && !doesPathCoincideWithTraces(candidatePath, otherNetTraces);
15587
+ ) && !isPathCollidingWithObstacles(candidatePath, this.obstacles) && !doesPathCoincideWithTraces(candidatePath, otherNetTraces) && otherNetTraces.every(
15588
+ (otherTrace) => countPathIntersections(candidatePath, otherTrace.tracePath) <= countPathIntersections(tracePath, otherTrace.tracePath)
15589
+ );
15578
15590
  }
15579
15591
  );
15580
15592
  const getOverlapCount = (candidatePath) => detectTraceLabelOverlap({
@@ -2,7 +2,11 @@ import { distance } from "@tscircuit/math-utils"
2
2
  import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
3
  import { tracePathContainsPoint } from "lib/solvers/RailNetLabelCornerPlacementSolver/geometry"
4
4
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
5
- import { getDistinctCoordinates, RAIL_ALIGNMENT_EPSILON } from "./geometry"
5
+ import {
6
+ getDistinctCoordinates,
7
+ nearlyEqual,
8
+ RAIL_ALIGNMENT_EPSILON,
9
+ } from "./geometry"
6
10
  import type { RailSegment } from "./types"
7
11
 
8
12
  const getTransitivelyConnectedTraceIds = (
@@ -59,13 +63,6 @@ export const getFixedLabelCoordinate = (
59
63
  .get(traceId)
60
64
  ?.pins.every((pin) => pin.chipId === group[0]!.componentId) === true,
61
65
  )
62
- const groupHasOnlySingleRailTraces = [...groupTraceIds].every(
63
- (traceId) => traceMap.get(traceId)?.tracePath.length === 4,
64
- )
65
- // A fixed label may lengthen the endpoint legs around one movable rail. A
66
- // trace with more elbows still has to pass the normal readability scoring.
67
- if (!groupHasOnlySingleRailTraces) return null
68
-
69
66
  const orientation = group[0]!.orientation
70
67
  const linkedLabels = getLabelsLinkedToRailGroup(
71
68
  group,
@@ -107,6 +104,18 @@ export const getFixedLabelCoordinate = (
107
104
  Math.abs(segment.coordinate - labelCoordinate) <=
108
105
  RAIL_ALIGNMENT_EPSILON,
109
106
  )?.coordinate ?? labelCoordinate
107
+ // A fixed label may lengthen the endpoint legs around one movable rail.
108
+ // A multi-turn backbone may share the group when it already lies on that
109
+ // coordinate, but moving its elbows still requires normal readability scoring.
110
+ if (
111
+ group.some(
112
+ (segment) =>
113
+ traceMap.get(segment.traceId)?.tracePath.length !== 4 &&
114
+ !nearlyEqual(segment.coordinate, coordinate),
115
+ )
116
+ ) {
117
+ return null
118
+ }
110
119
  const labelIsAnchoredToRoutedBackbone = anchoringLabels.some((label) =>
111
120
  label.mspConnectionPairIds.some(
112
121
  (traceId) => (traceMap.get(traceId)?.tracePath.length ?? 0) > 4,
@@ -1,11 +1,13 @@
1
1
  import type { Point } from "@tscircuit/math-utils"
2
2
  import type { GraphicsObject } from "graphics-debug"
3
3
  import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
4
+ import { countPathIntersections } from "lib/solvers/Example28Solver/geometry"
4
5
  import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
5
6
  import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
6
7
  import { isPathCollidingWithObstacles } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
7
8
  import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
8
9
  import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
10
+ import { getInteriorShortcutPaths } from "lib/solvers/TraceCleanupSolver/getInteriorShortcutPaths"
9
11
  import { preservesLabelAnchors } from "lib/solvers/TraceCleanupSolver/sameNetRailAlignment/preservesLabelAnchors"
10
12
  import { simplifyPath } from "lib/solvers/TraceCleanupSolver/simplifyPath"
11
13
  import { detectTraceLabelOverlap } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap"
@@ -82,7 +84,24 @@ export class TraceElbowTransitionSimplificationSolver extends BaseSolver {
82
84
  otherTrace.mspPairId !== trace.mspPairId &&
83
85
  otherTrace.globalConnNetId !== trace.globalConnNetId,
84
86
  )
87
+ const initialPathLength = getPathLength(tracePath)
85
88
  const candidateByPath = new Map<string, Point[]>()
89
+ // Consecutive label detours can leave a staircase between two clear legs.
90
+ // Keep equivalent-length shortcuts, as with the transition candidates below;
91
+ // shortening the route belongs to turn minimization. Terminal directions,
92
+ // label anchors, and collision checks still apply.
93
+ for (const candidate of getInteriorShortcutPaths(tracePath)) {
94
+ if (
95
+ Math.abs(getPathLength(candidate) - initialPathLength) >
96
+ PATH_LENGTH_EPSILON
97
+ ) {
98
+ continue
99
+ }
100
+ candidateByPath.set(
101
+ candidate.map((point) => `${point.x},${point.y}`).join(";"),
102
+ candidate,
103
+ )
104
+ }
86
105
  const completedReroutes = this.input.completedReroutes.filter(
87
106
  (completedReroute) =>
88
107
  completedReroute.initialTrace.mspPairId === trace.mspPairId,
@@ -145,8 +164,6 @@ export class TraceElbowTransitionSimplificationSolver extends BaseSolver {
145
164
  ({ label }) => `${label.globalConnNetId}:${label.netId}`,
146
165
  ),
147
166
  )
148
- const initialPathLength = getPathLength(tracePath)
149
-
150
167
  const validCandidates = [...candidateByPath.values()].filter(
151
168
  (candidatePath) => {
152
169
  const candidateTrace = { ...trace, tracePath: candidatePath }
@@ -175,7 +192,12 @@ export class TraceElbowTransitionSimplificationSolver extends BaseSolver {
175
192
  [candidateTrace],
176
193
  ) &&
177
194
  !isPathCollidingWithObstacles(candidatePath, this.obstacles) &&
178
- !doesPathCoincideWithTraces(candidatePath, otherNetTraces)
195
+ !doesPathCoincideWithTraces(candidatePath, otherNetTraces) &&
196
+ otherNetTraces.every(
197
+ (otherTrace) =>
198
+ countPathIntersections(candidatePath, otherTrace.tracePath) <=
199
+ countPathIntersections(tracePath, otherTrace.tracePath),
200
+ )
179
201
  )
180
202
  },
181
203
  )
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "url": "https://github.com/tscircuit/schematic-trace-solver.git"
6
6
  },
7
7
  "main": "dist/index.js",
8
- "version": "0.0.203",
8
+ "version": "0.0.204",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "start": "cosmos",
@@ -0,0 +1,7 @@
1
+ import type { InputProblem } from "lib/types/InputProblem"
2
+ import { PipelineDebugger } from "site/components/PipelineDebugger"
3
+ import input from "../../tests/repros/assets/repro-smart-lock-ble-module.input.json"
4
+
5
+ export default () => (
6
+ <PipelineDebugger inputProblem={input as unknown as InputProblem} />
7
+ )