@tscircuit/schematic-trace-solver 0.0.45 → 0.0.46

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.
@@ -6,14 +6,17 @@ import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/
6
6
  import type { InputProblem } from "lib/types/InputProblem"
7
7
  import { detectTraceLabelOverlap } from "../../detectTraceLabelOverlap"
8
8
  import { SingleOverlapSolver } from "../SingleOverlapSolver/SingleOverlapSolver"
9
+ import { isPointInsideLabel } from "./isPointInsideLabel"
10
+ import { visualizeDecomposition } from "./visualizeDecomposition"
9
11
 
10
12
  type Overlap = ReturnType<typeof detectTraceLabelOverlap>[0]
11
13
 
12
14
  // Define a type for the input of the internal overlap solver to avoid conflicts
13
- interface OverlapCollectionSolverInput {
15
+ export interface OverlapCollectionSolverInput {
14
16
  inputProblem: InputProblem
15
17
  traces: SolvedTracePath[]
16
- netLabelPlacements: NetLabelPlacement[]
18
+ initialNetLabelPlacements: NetLabelPlacement[]
19
+ mergedNetLabelPlacements: NetLabelPlacement[]
17
20
  mergedLabelNetIdMap: Record<string, Set<string>>
18
21
  }
19
22
 
@@ -23,7 +26,8 @@ interface OverlapCollectionSolverInput {
23
26
  */
24
27
  export class OverlapAvoidanceStepSolver extends BaseSolver {
25
28
  inputProblem: InputProblem
26
- netLabelPlacements: NetLabelPlacement[]
29
+ initialNetLabelPlacements: NetLabelPlacement[]
30
+ mergedNetLabelPlacements: NetLabelPlacement[]
27
31
  mergedLabelNetIdMap: Record<string, Set<string>>
28
32
 
29
33
  allTraces: SolvedTracePath[]
@@ -36,15 +40,21 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
36
40
  private overlapQueue: Overlap[] = []
37
41
  private recentlyFailed: Set<string> = new Set()
38
42
 
43
+ private currentlyProcessingOverlap: Overlap | null = null
44
+ private decomposedChildLabels: NetLabelPlacement[] | null = null
45
+
39
46
  constructor(solverInput: OverlapCollectionSolverInput) {
40
47
  super()
41
48
  this.inputProblem = solverInput.inputProblem
42
- this.netLabelPlacements = solverInput.netLabelPlacements
49
+ this.initialNetLabelPlacements = solverInput.initialNetLabelPlacements
50
+ this.mergedNetLabelPlacements = solverInput.mergedNetLabelPlacements
43
51
  this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap
44
52
  this.allTraces = [...solverInput.traces]
45
53
  }
46
54
 
47
55
  override _step() {
56
+ this.currentlyProcessingOverlap = null
57
+ this.decomposedChildLabels = null
48
58
  if (this.activeSubSolver) {
49
59
  this.activeSubSolver.step()
50
60
 
@@ -65,19 +75,14 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
65
75
  const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}`
66
76
  this.recentlyFailed.add(overlapId)
67
77
  this.activeSubSolver = null
78
+ } else {
68
79
  }
69
80
  return
70
81
  }
71
82
 
72
- const overlaps = detectTraceLabelOverlap(
73
- this.allTraces,
74
- this.netLabelPlacements,
75
- ).filter((o) => {
76
- const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId]
77
- if (originalNetIds) {
78
- return !originalNetIds.has(o.trace.globalConnNetId)
79
- }
80
- return o.trace.globalConnNetId !== o.label.globalConnNetId
83
+ const overlaps = detectTraceLabelOverlap({
84
+ traces: this.allTraces,
85
+ netLabels: this.mergedNetLabelPlacements,
81
86
  })
82
87
 
83
88
  if (overlaps.length === 0) {
@@ -85,6 +90,7 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
85
90
  return
86
91
  }
87
92
 
93
+ // Filter out overlaps that have recently failed to avoid infinite loops
88
94
  const nonFailedOverlaps = overlaps.filter((o) => {
89
95
  const overlapId = `${o.trace.mspPairId}-${o.label.globalConnNetId}`
90
96
  return !this.recentlyFailed.has(overlapId)
@@ -98,24 +104,104 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
98
104
  this.overlapQueue = nonFailedOverlaps
99
105
 
100
106
  const nextOverlap = this.overlapQueue.shift()
107
+ this.currentlyProcessingOverlap = nextOverlap ?? null
101
108
 
102
109
  if (nextOverlap) {
103
110
  const traceToFix = this.allTraces.find(
104
111
  (t) => t.mspPairId === nextOverlap.trace.mspPairId,
105
- )
106
- if (traceToFix) {
107
- const labelId = nextOverlap.label.globalConnNetId
108
- const detourCount = this.detourCountByLabel[labelId] || 0
109
- this.detourCountByLabel[labelId] = detourCount + 1
110
-
111
- this.activeSubSolver = new SingleOverlapSolver({
112
- trace: traceToFix,
113
- label: nextOverlap.label,
114
- problem: this.inputProblem,
115
- paddingBuffer: this.PADDING_BUFFER,
116
- detourCount,
117
- })
112
+ )!
113
+ const labelToAvoid = nextOverlap.label
114
+ const traceStartPoint = traceToFix.tracePath[0]
115
+
116
+ const originalNetIds =
117
+ this.mergedLabelNetIdMap[labelToAvoid.globalConnNetId]
118
+ const isSelfOverlap = originalNetIds?.has(traceToFix.globalConnNetId)
119
+
120
+ if (isSelfOverlap) {
121
+ const childLabels = this.initialNetLabelPlacements.filter((l) =>
122
+ originalNetIds.has(l.globalConnNetId),
123
+ )
124
+ this.decomposedChildLabels = childLabels
125
+ let actualOverlapLabel: NetLabelPlacement | null = null
126
+
127
+ for (const childLabel of childLabels) {
128
+ const overlapsWithChild = detectTraceLabelOverlap({
129
+ traces: [traceToFix],
130
+ netLabels: [childLabel],
131
+ })
132
+ if (overlapsWithChild.length > 0) {
133
+ actualOverlapLabel = childLabel
134
+ break
135
+ }
136
+ }
137
+
138
+ if (actualOverlapLabel) {
139
+ const labelId = actualOverlapLabel.globalConnNetId
140
+ const detourCount = this.detourCountByLabel[labelId] || 0
141
+ this.detourCountByLabel[labelId] = detourCount + 1
142
+ this.activeSubSolver = new SingleOverlapSolver({
143
+ trace: traceToFix,
144
+ label: actualOverlapLabel,
145
+ problem: this.inputProblem,
146
+ paddingBuffer: this.PADDING_BUFFER,
147
+ detourCount,
148
+ })
149
+ } else {
150
+ const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`
151
+ this.recentlyFailed.add(overlapId)
152
+ }
153
+ return
154
+ }
155
+
156
+ if (
157
+ originalNetIds &&
158
+ isPointInsideLabel({ point: traceStartPoint, label: labelToAvoid })
159
+ ) {
160
+ const childLabels = this.initialNetLabelPlacements.filter((l) =>
161
+ originalNetIds.has(l.globalConnNetId),
162
+ )
163
+ this.decomposedChildLabels = childLabels
164
+ let actualOverlapLabel: NetLabelPlacement | null = null
165
+
166
+ for (const childLabel of childLabels) {
167
+ const overlapsWithChild = detectTraceLabelOverlap({
168
+ traces: [traceToFix],
169
+ netLabels: [childLabel],
170
+ })
171
+ if (overlapsWithChild.length > 0) {
172
+ actualOverlapLabel = childLabel
173
+ break
174
+ }
175
+ }
176
+
177
+ if (actualOverlapLabel) {
178
+ const labelId = actualOverlapLabel.globalConnNetId
179
+ const detourCount = this.detourCountByLabel[labelId] || 0
180
+ this.detourCountByLabel[labelId] = detourCount + 1
181
+ this.activeSubSolver = new SingleOverlapSolver({
182
+ trace: traceToFix,
183
+ label: actualOverlapLabel,
184
+ problem: this.inputProblem,
185
+ paddingBuffer: this.PADDING_BUFFER,
186
+ detourCount,
187
+ })
188
+ } else {
189
+ const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`
190
+ this.recentlyFailed.add(overlapId)
191
+ }
192
+ return
118
193
  }
194
+
195
+ const labelId = labelToAvoid.globalConnNetId
196
+ const detourCount = this.detourCountByLabel[labelId] || 0
197
+ this.detourCountByLabel[labelId] = detourCount + 1
198
+ this.activeSubSolver = new SingleOverlapSolver({
199
+ trace: traceToFix,
200
+ label: labelToAvoid,
201
+ problem: this.inputProblem,
202
+ paddingBuffer: this.PADDING_BUFFER,
203
+ detourCount,
204
+ })
119
205
  }
120
206
  }
121
207
 
@@ -130,7 +216,7 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
130
216
  if (this.activeSubSolver) {
131
217
  return this.activeSubSolver.visualize()
132
218
  }
133
- // When idle, show all the traces
219
+
134
220
  const graphics = visualizeInputProblem(this.inputProblem)
135
221
  if (!graphics.lines) graphics.lines = []
136
222
  for (const trace of this.allTraces) {
@@ -139,6 +225,43 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
139
225
  strokeColor: "purple",
140
226
  })
141
227
  }
228
+
229
+ if (this.currentlyProcessingOverlap) {
230
+ const { trace, label } = this.currentlyProcessingOverlap
231
+
232
+ // Highlight the colliding trace
233
+ graphics.lines!.push({
234
+ points: trace.tracePath,
235
+ strokeColor: "red",
236
+ })
237
+
238
+ if (this.decomposedChildLabels) {
239
+ visualizeDecomposition({
240
+ decomposedChildLabels: this.decomposedChildLabels,
241
+ collidingTrace: trace,
242
+ mergedLabel: label,
243
+ graphics,
244
+ })
245
+ } else {
246
+ // Standard case: highlight the entire label
247
+ if (!graphics.rects) graphics.rects = []
248
+ graphics.rects.push({
249
+ center: label.center,
250
+ width: label.width,
251
+ height: label.height,
252
+ fill: "yellow",
253
+ })
254
+ if (!graphics.texts) graphics.texts = []
255
+ graphics.texts.push({
256
+ x: label.center.x,
257
+ y: label.center.y + label.height / 2 + 0.5,
258
+ text: `COLLISION: Trace ${trace.mspPairId} vs Label ${label.globalConnNetId}`,
259
+ fontSize: 0.3,
260
+ color: "red",
261
+ })
262
+ }
263
+ }
264
+
142
265
  return graphics
143
266
  }
144
267
  }
@@ -0,0 +1,23 @@
1
+ import type { Point } from "graphics-debug"
2
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
+ import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
4
+
5
+ /**
6
+ * Checks if a point is inside the bounding box of a net label.
7
+ */
8
+ export const isPointInsideLabel = ({
9
+ point,
10
+ label,
11
+ }: {
12
+ point: Point
13
+ label: NetLabelPlacement
14
+ }): boolean => {
15
+ const bounds = getRectBounds(label.center, label.width, label.height)
16
+
17
+ return (
18
+ point.x >= bounds.minX &&
19
+ point.x <= bounds.maxX &&
20
+ point.y >= bounds.minY &&
21
+ point.y <= bounds.maxY
22
+ )
23
+ }
@@ -0,0 +1,54 @@
1
+ import type { GraphicsObject } from "graphics-debug"
2
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
3
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
4
+
5
+ interface VisualizeDecompositionParams {
6
+ decomposedChildLabels: NetLabelPlacement[]
7
+ collidingTrace: SolvedTracePath
8
+ mergedLabel: NetLabelPlacement
9
+ graphics: GraphicsObject
10
+ }
11
+
12
+ /**
13
+ * Visualizes the decomposition of a merged label during overlap avoidance.
14
+ * It draws individual child labels with distinct colors based on their relation
15
+ * to the colliding trace and adds a textual report to the graphics.
16
+ *
17
+ * @param {VisualizeDecompositionParams} params - The parameters for visualization.
18
+ * @param {NetLabelPlacement[]} params.decomposedChildLabels - The individual labels that make up the merged group.
19
+ * @param {SolvedTracePath} params.collidingTrace - The trace that is currently colliding with the merged label.
20
+ * @param {NetLabelPlacement} params.mergedLabel - The original merged label that is being decomposed.
21
+ * @param {GraphicsObject} params.graphics - The graphics object to which the visualization elements will be added.
22
+ * @returns {GraphicsObject} The modified graphics object with decomposition visualization elements.
23
+ */
24
+ export const visualizeDecomposition = (
25
+ params: VisualizeDecompositionParams,
26
+ ): GraphicsObject => {
27
+ const { decomposedChildLabels, collidingTrace, mergedLabel, graphics } =
28
+ params
29
+
30
+ if (!graphics.rects) graphics.rects = []
31
+ if (!graphics.texts) graphics.texts = []
32
+
33
+ for (const childLabel of decomposedChildLabels) {
34
+ const isOwnLabel =
35
+ childLabel.globalConnNetId === collidingTrace.globalConnNetId
36
+
37
+ graphics.rects.push({
38
+ center: childLabel.center,
39
+ width: childLabel.width,
40
+ height: childLabel.height,
41
+ fill: isOwnLabel ? "green" : "red", // Green for own label, red for others
42
+ })
43
+ }
44
+
45
+ graphics.texts.push({
46
+ x: mergedLabel.center.x,
47
+ y: mergedLabel.center.y + mergedLabel.height / 2 + 0.5,
48
+ text: `DECOMPOSITION: Trace ${collidingTrace.mspPairId} vs Merged Label ${mergedLabel.globalConnNetId}`,
49
+ fontSize: 0.3,
50
+ color: "blue",
51
+ })
52
+
53
+ return graphics
54
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/schematic-trace-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.45",
4
+ "version": "0.0.46",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -160,28 +160,28 @@ x-" data-x="-0.42" data-y="-0.1" cx="279.0224461302513" cy="382.68590086027035"
160
160
  <rect data-type="rect" data-label="schematic_component_6" data-x="0" data-y="0" x="277.802876074604" y="349.51359534666426" width="43.416693981043295" height="56.58805058203393" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.020499027410714285" />
161
161
  </g>
162
162
  <g>
163
- <rect data-type="rect" data-label="connectivity_net0" data-x="-4.78" data-y="2.7600000000000002" x="55.35329792453959" y="238.288806271632" width="21.95226100165104" height="9.75656044517828" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
163
+ <rect data-type="rect" data-label="connectivity_net2" data-x="6" data-y="0.776" x="587.3297561978845" y="328.976035609564" width="9.756560445178138" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
164
164
  </g>
165
165
  <g>
166
- <rect data-type="rect" data-label="connectivity_net3" data-x="3" data-y="-0.93" x="440.9813495202105" y="415.85820637387644" width="9.756560445178309" height="14.634840667767435" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
166
+ <rect data-type="rect" data-label="connectivity_net2" data-x="3.7199999999999998" data-y="3.225" x="476.10496712285226" y="209.50695295835615" width="9.756560445178252" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
167
167
  </g>
168
168
  <g>
169
- <rect data-type="rect" data-label="connectivity_net3" data-x="6" data-y="-0.7010000000000001" x="587.3297561978845" y="404.68694466414735" width="9.756560445178138" height="14.634840667767378" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
169
+ <rect data-type="rect" data-label="connectivity_net1" data-x="0.3" data-y="0.8059999999999999" x="309.2677835103039" y="327.51255154278726" width="9.756560445178309" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
170
170
  </g>
171
171
  <g>
172
- <rect data-type="rect" data-label="connectivity_net3" data-x="-4.995" data-y="-0.7958008" x="50.96284572420936" y="409.31159334140364" width="9.756560445178224" height="14.634840667767378" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
172
+ <rect data-type="rect" data-label="connectivity_net1" data-x="0.78" data-y="3.1950000000000003" x="332.68352857873174" y="210.97043702513287" width="9.756560445178252" height="21.952261001651124" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
173
173
  </g>
174
174
  <g>
175
- <rect data-type="rect" data-label="connectivity_net1" data-x="0.78" data-y="3.1950000000000003" x="332.68352857873174" y="210.97043702513287" width="9.756560445178252" height="21.952261001651124" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
175
+ <rect data-type="rect" data-label="connectivity_net3" data-x="6" data-y="-0.7010000000000001" x="587.3297561978845" y="404.68694466414735" width="9.756560445178138" height="14.634840667767378" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
176
176
  </g>
177
177
  <g>
178
- <rect data-type="rect" data-label="merged-group-M1-y+" data-x="-0.39186722499999993" data-y="0.4655" x="241.7653395028014" y="327.51255154278726" width="77.25900445268081" height="55.17334931748309" fill="hsl(296, 100%, 50%, 0.3)" stroke="hsl(296, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
178
+ <rect data-type="rect" data-label="connectivity_net3" data-x="3" data-y="-0.93" x="440.9813495202105" y="415.85820637387644" width="9.756560445178309" height="14.634840667767435" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
179
179
  </g>
180
180
  <g>
181
- <rect data-type="rect" data-label="connectivity_net2" data-x="3.7199999999999998" data-y="3.225" x="476.10496712285226" y="209.50695295835615" width="9.756560445178252" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
181
+ <rect data-type="rect" data-label="connectivity_net0" data-x="-4.78" data-y="2.7600000000000002" x="55.35329792453959" y="238.288806271632" width="21.95226100165104" height="9.75656044517828" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
182
182
  </g>
183
183
  <g>
184
- <rect data-type="rect" data-label="connectivity_net2" data-x="6" data-y="0.776" x="587.3297561978845" y="328.976035609564" width="9.756560445178138" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
184
+ <rect data-type="rect" data-label="connectivity_net4" data-x="-1.08373445" data-y="0.12500000000000014" x="241.7653395028014" y="360.73363985861926" width="9.75656044517828" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
185
185
  </g>
186
186
  <g id="crosshair" style="display: none">
187
187
  <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />