@tscircuit/schematic-trace-solver 0.0.41 → 0.0.43

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 (58) hide show
  1. package/dist/index.d.ts +118 -8
  2. package/dist/index.js +1067 -748
  3. package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +48 -16
  4. package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +133 -0
  5. package/lib/solvers/TraceCleanupSolver/balanceZShapes.ts +200 -0
  6. package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/hasCollisions.ts +1 -1
  7. package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/hasCollisionsWithLabels.ts +1 -1
  8. package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +76 -0
  9. package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/simplifyPath.ts +1 -1
  10. package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/turnMinimization.ts +0 -2
  11. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +74 -192
  12. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +13 -24
  13. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts +205 -0
  14. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +144 -0
  15. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +117 -0
  16. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts +104 -0
  17. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +24 -154
  18. package/package.json +1 -1
  19. package/site/examples/example26.page.tsx +4 -0
  20. package/tests/assets/1.input.json +379 -0
  21. package/tests/assets/2.input.json +330 -0
  22. package/tests/assets/3.input.json +388 -0
  23. package/tests/assets/MergedNetLabelObstacles.test.input.json +154 -0
  24. package/tests/assets/OverlapAvoidanceStepSolver.test.input.json +682 -0
  25. package/tests/assets/SingleOverlapSolver.test.input.json +170 -0
  26. package/tests/assets/TraceCleanupSolver.test.input.json +216 -0
  27. package/tests/assets/TraceLabelOverlapAvoidanceSolver.test.input.json +424 -0
  28. package/tests/assets/example26.json +1206 -0
  29. package/tests/examples/__snapshots__/example02.snap.svg +1 -1
  30. package/tests/examples/__snapshots__/example09.snap.svg +34 -34
  31. package/tests/examples/__snapshots__/example11.snap.svg +13 -13
  32. package/tests/examples/__snapshots__/example12.snap.svg +3 -3
  33. package/tests/examples/__snapshots__/example13.snap.svg +12 -12
  34. package/tests/examples/__snapshots__/example16.snap.svg +4 -4
  35. package/tests/examples/__snapshots__/example19.snap.svg +9 -9
  36. package/tests/examples/__snapshots__/example28.snap.svg +3 -3
  37. package/tests/examples/__snapshots__/example29.snap.svg +1208 -0
  38. package/tests/examples/example29.test.ts +13 -0
  39. package/tests/solvers/TraceCleanupSolver/TraceCleanupSolver.test.ts +19 -0
  40. package/tests/solvers/TraceCleanupSolver/__snapshots__/TraceCleanupSolver.snap.svg +119 -0
  41. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts +18 -0
  42. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +243 -0
  43. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView01.snap.svg +223 -0
  44. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView02.snap.svg +205 -0
  45. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView03.snap.svg +184 -0
  46. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView01.test.ts +29 -0
  47. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView02.test.ts +27 -0
  48. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts +39 -0
  49. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts +14 -0
  50. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts +20 -0
  51. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts +10 -0
  52. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg +140 -0
  53. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg +216 -0
  54. package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg +125 -0
  55. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts +0 -207
  56. package/lib/solvers/TraceLabelOverlapAvoidanceSolver/minimizeTurnsWithFilteredLabels.ts +0 -66
  57. /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/countTurns.ts +0 -0
  58. /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver → TraceCleanupSolver}/tryConnectPoints.ts +0 -0
@@ -1,207 +0,0 @@
1
- import type { Point } from "graphics-debug"
2
- import type { InputProblem } from "lib/types/InputProblem"
3
- import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
4
- import { getObstacleRects } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
5
- import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
6
- import { segmentIntersectsRect } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
7
- import { simplifyPath } from "./simplifyPath"
8
-
9
- export const balanceLShapes = ({
10
- traces,
11
- problem,
12
- allLabelPlacements,
13
- }: {
14
- traces: SolvedTracePath[]
15
- problem: InputProblem
16
- allLabelPlacements: NetLabelPlacement[]
17
- }): SolvedTracePath[] | null => {
18
- const TOLERANCE = 1e-5
19
- let changesMade = false
20
-
21
- for (const trace of traces) {
22
- if (trace.tracePath.length === 4) {
23
- const [p0, p1, p2, p3] = trace.tracePath
24
- const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y
25
- const isVHVShape = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x
26
-
27
- const isCollinearHorizontal =
28
- p0.y === p1.y && p1.y === p2.y && p2.y === p3.y
29
- const isCollinearVertical =
30
- p0.x === p1.x && p1.x === p2.x && p2.x === p3.x
31
- const isCollinear = isCollinearHorizontal || isCollinearVertical
32
-
33
- let isSameDirection = false
34
- if (isHVHShape) {
35
- isSameDirection = Math.sign(p1.x - p0.x) === Math.sign(p3.x - p2.x)
36
- } else if (isVHVShape) {
37
- isSameDirection = Math.sign(p1.y - p0.y) === Math.sign(p3.y - p2.y)
38
- }
39
-
40
- const isValidZShape =
41
- (isHVHShape || isVHVShape) && !isCollinear && isSameDirection
42
-
43
- if (!isValidZShape) {
44
- return null
45
- }
46
- }
47
- }
48
-
49
- const obstacles = getObstacleRects(problem).map((obs) => ({
50
- ...obs,
51
- minX: obs.minX + TOLERANCE,
52
- maxX: obs.maxX - TOLERANCE,
53
- minY: obs.minY + TOLERANCE,
54
- maxY: obs.maxY - TOLERANCE,
55
- }))
56
-
57
- const segmentIntersectsAnyRect = (
58
- p1: Point,
59
- p2: Point,
60
- rects: any[],
61
- ): boolean => {
62
- for (const rect of rects) {
63
- if (segmentIntersectsRect(p1, p2, rect)) {
64
- return true
65
- }
66
- }
67
- return false
68
- }
69
-
70
- const getLabelBounds = (labels: NetLabelPlacement[], traceNetId: string) => {
71
- const filteredLabels = labels.filter(
72
- (label) => label.globalConnNetId !== traceNetId,
73
- )
74
-
75
- return filteredLabels.map((nl) => ({
76
- minX: nl.center.x - nl.width / 2 + TOLERANCE,
77
- maxX: nl.center.x + nl.width / 2 - TOLERANCE,
78
- minY: nl.center.y - nl.height / 2 + TOLERANCE,
79
- maxY: nl.center.y + nl.height / 2 - TOLERANCE,
80
- }))
81
- }
82
-
83
- const newTraces = traces.map((trace) => {
84
- let newPath = [...trace.tracePath]
85
-
86
- if (newPath.length < 4) {
87
- return { ...trace }
88
- }
89
-
90
- const labelBounds = getLabelBounds(
91
- allLabelPlacements,
92
- trace.globalConnNetId,
93
- )
94
-
95
- if (newPath.length === 4) {
96
- const [p0, p1, p2, p3] = newPath
97
- let p1New: Point, p2New: Point
98
-
99
- const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y
100
-
101
- if (isHVHShape) {
102
- const idealX = (p0.x + p3.x) / 2
103
- p1New = { x: idealX, y: p1.y }
104
- p2New = { x: idealX, y: p2.y }
105
- } else {
106
- const idealY = (p0.y + p3.y) / 2
107
- p1New = { x: p1.x, y: idealY }
108
- p2New = { x: p2.x, y: idealY }
109
- }
110
-
111
- const collides =
112
- segmentIntersectsAnyRect(p0, p1New, obstacles) ||
113
- segmentIntersectsAnyRect(p1New, p2New, obstacles) ||
114
- segmentIntersectsAnyRect(p2New, p3, obstacles) ||
115
- segmentIntersectsAnyRect(p0, p1New, labelBounds) ||
116
- segmentIntersectsAnyRect(p1New, p2New, labelBounds) ||
117
- segmentIntersectsAnyRect(p2New, p3, labelBounds)
118
-
119
- if (!collides) {
120
- newPath[1] = p1New
121
- newPath[2] = p2New
122
- changesMade = true
123
- }
124
-
125
- return { ...trace, tracePath: simplifyPath(newPath) }
126
- }
127
-
128
- for (let i = 1; i < newPath.length - 4; i++) {
129
- const p1 = newPath[i]
130
- const p2 = newPath[i + 1]
131
- const p3 = newPath[i + 2]
132
- const p4 = newPath[i + 3]
133
-
134
- const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y
135
- const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x
136
-
137
- const isCollinearHorizontal =
138
- p1.y === p2.y && p2.y === p3.y && p3.y === p4.y
139
- const isCollinearVertical =
140
- p1.x === p2.x && p2.x === p3.x && p3.x === p4.x
141
- const isCollinear = isCollinearHorizontal || isCollinearVertical
142
-
143
- let isSameDirection = false
144
- if (isHVHZShape) {
145
- isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x)
146
- } else if (isVHVZShape) {
147
- isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y)
148
- }
149
-
150
- const isValidZShape =
151
- (isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection
152
-
153
- if (!isValidZShape) {
154
- continue
155
- }
156
-
157
- let p2New: Point, p3New: Point
158
- const len1Original = isHVHZShape
159
- ? Math.abs(p1.x - p2.x)
160
- : Math.abs(p1.y - p2.y)
161
- const len2Original = isHVHZShape
162
- ? Math.abs(p3.x - p4.x)
163
- : Math.abs(p3.y - p4.y)
164
-
165
- if (Math.abs(len1Original - len2Original) < 0.001) {
166
- continue
167
- }
168
-
169
- if (isHVHZShape) {
170
- const idealX = (p1.x + p4.x) / 2
171
- p2New = { x: idealX, y: p2.y }
172
- p3New = { x: idealX, y: p3.y }
173
- } else {
174
- const idealY = (p1.y + p4.y) / 2
175
- p2New = { x: p2.x, y: idealY }
176
- p3New = { x: p3.x, y: idealY }
177
- }
178
-
179
- const collides =
180
- segmentIntersectsAnyRect(p1, p2New, obstacles) ||
181
- segmentIntersectsAnyRect(p2New, p3New, obstacles) ||
182
- segmentIntersectsAnyRect(p3New, p4, obstacles) ||
183
- segmentIntersectsAnyRect(p1, p2New, labelBounds) ||
184
- segmentIntersectsAnyRect(p2New, p3New, labelBounds) ||
185
- segmentIntersectsAnyRect(p3New, p4, labelBounds)
186
-
187
- if (!collides) {
188
- newPath[i + 1] = p2New
189
- newPath[i + 2] = p3New
190
- changesMade = true
191
- i = 0
192
- }
193
- }
194
-
195
- const finalSimplifiedPath = simplifyPath(newPath)
196
- return {
197
- ...trace,
198
- tracePath: finalSimplifiedPath,
199
- }
200
- })
201
-
202
- if (changesMade) {
203
- return newTraces
204
- } else {
205
- return null
206
- }
207
- }
@@ -1,66 +0,0 @@
1
- import type { InputProblem } from "lib/types/InputProblem"
2
- import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
3
- import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
- import { getObstacleRects } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
5
- import { minimizeTurns } from "./turnMinimization"
6
-
7
- export const minimizeTurnsWithFilteredLabels = ({
8
- traces,
9
- problem,
10
- allLabelPlacements,
11
- mergedLabelNetIdMap,
12
- paddingBuffer,
13
- }: {
14
- traces: SolvedTracePath[]
15
- problem: InputProblem
16
- allLabelPlacements: NetLabelPlacement[]
17
- mergedLabelNetIdMap: Record<string, Set<string>>
18
- paddingBuffer: number
19
- }): SolvedTracePath[] | null => {
20
- let changesMade = false
21
- const obstacles = getObstacleRects(problem)
22
-
23
- const newTraces = traces.map((trace) => {
24
- const originalPath = trace.tracePath
25
- const filteredLabels = allLabelPlacements.filter((label) => {
26
- const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId]
27
- if (originalNetIds) {
28
- return !originalNetIds.has(trace.globalConnNetId)
29
- }
30
- return label.globalConnNetId !== trace.globalConnNetId
31
- })
32
-
33
- const labelBounds = filteredLabels.map((nl) => ({
34
- minX: nl.center.x - nl.width / 2 - paddingBuffer,
35
- maxX: nl.center.x + nl.width / 2 + paddingBuffer,
36
- minY: nl.center.y - nl.height / 2 - paddingBuffer,
37
- maxY: nl.center.y + nl.height / 2 + paddingBuffer,
38
- }))
39
-
40
- const newPath = minimizeTurns({
41
- path: originalPath,
42
- obstacles,
43
- labelBounds,
44
- })
45
-
46
- if (
47
- newPath.length !== originalPath.length ||
48
- newPath.some(
49
- (p, i) => p.x !== originalPath[i].x || p.y !== originalPath[i].y,
50
- )
51
- ) {
52
- changesMade = true
53
- }
54
-
55
- return {
56
- ...trace,
57
- tracePath: newPath,
58
- }
59
- })
60
-
61
- if (changesMade) {
62
- return newTraces
63
- } else {
64
- return null
65
- }
66
- }