@tscircuit/schematic-trace-solver 0.0.62 → 0.0.64

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 (24) hide show
  1. package/dist/index.d.ts +7 -0
  2. package/dist/index.js +126 -52
  3. package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +67 -36
  4. package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +13 -0
  5. package/lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts +8 -0
  6. package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +24 -0
  7. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts +7 -1
  8. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts +9 -4
  9. package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts +3 -0
  10. package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +9 -13
  11. package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts +19 -0
  12. package/lib/types/InputProblem.ts +1 -0
  13. package/package.json +1 -1
  14. package/tests/assets/example41.json +2 -1
  15. package/tests/examples/__snapshots__/example34.snap.svg +23 -19
  16. package/tests/examples/__snapshots__/example41.snap.svg +2 -2
  17. package/tests/examples/example34.test.ts +8 -0
  18. package/tests/fixtures/traceLabelCollisions.ts +42 -0
  19. package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +273 -0
  20. package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +113 -0
  21. package/tests/repros/assets/repro-netlabel-connector-through-label.input.json +139 -0
  22. package/tests/repros/assets/repro126-trace-overlap-box-resistor.input.json +74 -0
  23. package/tests/repros/netlabel-connector-through-rail-label.test.ts +19 -0
  24. package/tests/repros/trace-overlap-box-resistor.test.ts +14 -0
@@ -280,6 +280,26 @@ export class NetLabelPlacementSolver extends BaseSolver {
280
280
  )?.netLabelWidth
281
281
  }
282
282
 
283
+ private getNetLabelHeightForGroup(
284
+ group: OverlappingSameNetTraceGroup,
285
+ ): number | undefined {
286
+ if (group.netId) {
287
+ const ncHeight = this.inputProblem.netConnections.find(
288
+ (nc) => nc.netId === group.netId,
289
+ )?.netLabelHeight
290
+ if (ncHeight !== undefined) return ncHeight
291
+ }
292
+
293
+ const pinIds = group.overlappingTraces?.pins.map((p) => p.pinId) ?? []
294
+ if (group.portOnlyPinId) {
295
+ pinIds.push(group.portOnlyPinId)
296
+ }
297
+
298
+ return this.inputProblem.netConnections.find((nc) =>
299
+ nc.pinIds.some((pid) => pinIds.includes(pid)),
300
+ )?.netLabelHeight
301
+ }
302
+
283
303
  override _step() {
284
304
  if (this.activeSubSolver?.solved) {
285
305
  this.netLabelPlacements.push(this.activeSubSolver.netLabelPlacement!)
@@ -304,12 +324,14 @@ export class NetLabelPlacementSolver extends BaseSolver {
304
324
  ) {
305
325
  this.triedAnyOrientationFallbackForCurrentGroup = true
306
326
  const netLabelWidth = this.getNetLabelWidthForGroup(this.currentGroup)
327
+ const netLabelHeight = this.getNetLabelHeightForGroup(this.currentGroup)
307
328
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
308
329
  inputProblem: this.inputProblem,
309
330
  inputTraceMap: this.inputTraceMap,
310
331
  overlappingSameNetTraceGroup: this.currentGroup,
311
332
  availableOrientations: fullOrients,
312
333
  netLabelWidth,
334
+ netLabelHeight,
313
335
  })
314
336
  return
315
337
  }
@@ -340,6 +362,7 @@ export class NetLabelPlacementSolver extends BaseSolver {
340
362
  this.triedAnyOrientationFallbackForCurrentGroup = false
341
363
 
342
364
  const netLabelWidth = this.getNetLabelWidthForGroup(this.currentGroup)
365
+ const netLabelHeight = this.getNetLabelHeightForGroup(this.currentGroup)
343
366
 
344
367
  this.activeSubSolver = new SingleNetLabelPlacementSolver({
345
368
  inputProblem: this.inputProblem,
@@ -349,6 +372,7 @@ export class NetLabelPlacementSolver extends BaseSolver {
349
372
  netId
350
373
  ] ?? ["x+", "x-", "y+", "y-"],
351
374
  netLabelWidth,
375
+ netLabelHeight,
352
376
  })
353
377
  }
354
378
 
@@ -58,8 +58,9 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
58
58
 
59
59
  chipObstacleSpatialIndex: ChipObstacleSpatialIndex
60
60
 
61
- // Optional override for the width of the net label (per netId)
61
+ // Optional override for the width/height of the net label (per netId)
62
62
  netLabelWidth?: number
63
+ netLabelHeight?: number
63
64
 
64
65
  netLabelPlacement: NetLabelPlacement | null = null
65
66
  testedCandidates: Array<{
@@ -79,6 +80,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
79
80
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup
80
81
  availableOrientations: FacingDirection[]
81
82
  netLabelWidth?: number
83
+ netLabelHeight?: number
82
84
  }) {
83
85
  super()
84
86
  this.inputProblem = params.inputProblem
@@ -86,6 +88,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
86
88
  this.overlappingSameNetTraceGroup = params.overlappingSameNetTraceGroup
87
89
  this.availableOrientations = params.availableOrientations
88
90
  this.netLabelWidth = params.netLabelWidth
91
+ this.netLabelHeight = params.netLabelHeight
89
92
 
90
93
  this.chipObstacleSpatialIndex =
91
94
  params.inputProblem._chipObstacleSpatialIndex ??
@@ -101,6 +104,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
101
104
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
102
105
  availableOrientations: this.availableOrientations,
103
106
  netLabelWidth: this.netLabelWidth,
107
+ netLabelHeight: this.netLabelHeight,
104
108
  }
105
109
  }
106
110
 
@@ -119,6 +123,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
119
123
  overlappingSameNetTraceGroup: this.overlappingSameNetTraceGroup,
120
124
  availableOrientations: this.availableOrientations,
121
125
  netLabelWidth: this.netLabelWidth,
126
+ netLabelHeight: this.netLabelHeight,
122
127
  })
123
128
  this.testedCandidates.push(...res.testedCandidates)
124
129
  if (res.placement) {
@@ -230,6 +235,7 @@ export class SingleNetLabelPlacementSolver extends BaseSolver {
230
235
  const { width, height } = getDimsForOrientation({
231
236
  orientation,
232
237
  netLabelWidth: this.netLabelWidth,
238
+ netLabelHeight: this.netLabelHeight,
233
239
  })
234
240
  const center = getCenterFromAnchor(
235
241
  anchor,
@@ -6,23 +6,28 @@ export const NET_LABEL_HORIZONTAL_HEIGHT = 0.2
6
6
  export function getDimsForOrientation(params: {
7
7
  orientation: FacingDirection
8
8
  netLabelWidth?: number
9
+ netLabelHeight?: number
9
10
  }) {
10
- const { orientation, netLabelWidth } = params
11
+ const { orientation, netLabelWidth, netLabelHeight } = params
11
12
  const horizWidth =
12
13
  typeof netLabelWidth === "number"
13
14
  ? netLabelWidth
14
15
  : NET_LABEL_HORIZONTAL_WIDTH
16
+ const horizHeight =
17
+ typeof netLabelHeight === "number"
18
+ ? netLabelHeight
19
+ : NET_LABEL_HORIZONTAL_HEIGHT
15
20
 
16
21
  if (orientation === "y+" || orientation === "y-") {
17
22
  return {
18
- // Rotated, so width/height swap
19
- width: NET_LABEL_HORIZONTAL_HEIGHT,
23
+ // Rotated: horizontal length = netLabelHeight, vertical length = netLabelWidth
24
+ width: horizHeight,
20
25
  height: horizWidth,
21
26
  }
22
27
  }
23
28
  return {
24
29
  width: horizWidth,
25
- height: NET_LABEL_HORIZONTAL_HEIGHT,
30
+ height: horizHeight,
26
31
  }
27
32
  }
28
33
 
@@ -22,6 +22,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
22
22
  overlappingSameNetTraceGroup: OverlappingSameNetTraceGroup
23
23
  availableOrientations: FacingDirection[]
24
24
  netLabelWidth?: number
25
+ netLabelHeight?: number
25
26
  }): {
26
27
  placement: NetLabelPlacement | null
27
28
  testedCandidates: Array<{
@@ -43,6 +44,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
43
44
  overlappingSameNetTraceGroup,
44
45
  availableOrientations,
45
46
  netLabelWidth,
47
+ netLabelHeight,
46
48
  } = params
47
49
 
48
50
  const pinId = overlappingSameNetTraceGroup.portOnlyPinId
@@ -103,6 +105,7 @@ export function solveNetLabelPlacementForPortOnlyPin(params: {
103
105
  const { width, height } = getDimsForOrientation({
104
106
  orientation,
105
107
  netLabelWidth,
108
+ netLabelHeight,
106
109
  })
107
110
  // Place label fully outside the chip: shift center slightly outward
108
111
  const baseCenter = getCenterFromAnchor(anchor, orientation, width, height)
@@ -72,31 +72,27 @@ function buildMergedObstacleLabel(
72
72
  }
73
73
  }
74
74
 
75
- interface TraceInnerLabelOverlap {
75
+ interface TraceLabelOverlap {
76
76
  trace: SolvedTracePath
77
77
  label: NetLabelPlacement
78
78
  }
79
79
 
80
80
  /**
81
- * Detects overlaps where a trace's INTERIOR (non-endpoint) segments cross a label.
82
- * Traces are allowed to boundary a label at their endpoints (pin locations),
83
- * just not allowed to cross through it in the middle.
81
+ * Detects overlaps where a trace segment intersects a label from another net.
82
+ * Point-only contact is allowed by segmentIntersectsRect; positive overlap on
83
+ * a label edge is still a visual collision and should be rerouted.
84
84
  */
85
- function detectInnerTraceLabelOverlaps(
85
+ function detectTraceLabelOverlaps(
86
86
  traces: SolvedTracePath[],
87
87
  labels: NetLabelPlacement[],
88
- ): TraceInnerLabelOverlap[] {
89
- const overlaps: TraceInnerLabelOverlap[] = []
88
+ ): TraceLabelOverlap[] {
89
+ const overlaps: TraceLabelOverlap[] = []
90
90
  for (const trace of traces) {
91
91
  for (const label of labels) {
92
92
  if (trace.globalConnNetId === label.globalConnNetId) continue
93
93
  const bounds = getRectBounds(label.center, label.width, label.height)
94
94
  const path = trace.tracePath
95
- // Skip first and last segments trace endpoints (pins) may legitimately
96
- // sit inside or adjacent to labels of neighbouring nets.
97
- const innerStart = 1
98
- const innerEnd = path.length - 2
99
- for (let i = innerStart; i < innerEnd; i++) {
95
+ for (let i = 0; i < path.length - 1; i++) {
100
96
  if (segmentIntersectsRect(path[i]!, path[i + 1]!, bounds)) {
101
97
  overlaps.push({ trace, label })
102
98
  break
@@ -183,7 +179,7 @@ export class NetLabelTraceCollisionSolver extends BaseSolver {
183
179
  return
184
180
  }
185
181
 
186
- const overlaps = detectInnerTraceLabelOverlaps(
182
+ const overlaps = detectTraceLabelOverlaps(
187
183
  this.outputTraces,
188
184
  this.outputNetLabelPlacements,
189
185
  )
@@ -30,6 +30,7 @@ export const generateCandidatesAlongTrace = (params: {
30
30
  getTraceVertexDistances(traceLocation.trace),
31
31
  )
32
32
  const netLabelWidth = getNetLabelWidth(inputProblem, label)
33
+ const netLabelHeight = getNetLabelHeight(inputProblem, label)
33
34
  const candidates: LabelCandidate[] = []
34
35
  const seenCandidateKeys = new Set<string>()
35
36
 
@@ -55,6 +56,7 @@ export const generateCandidatesAlongTrace = (params: {
55
56
  point,
56
57
  orientation,
57
58
  netLabelWidth,
59
+ netLabelHeight,
58
60
  traceLocation,
59
61
  pathDistance,
60
62
  label,
@@ -109,6 +111,7 @@ const createCandidate = (params: {
109
111
  point: Point
110
112
  orientation: FacingDirection
111
113
  netLabelWidth: number
114
+ netLabelHeight?: number
112
115
  traceLocation: TraceLocation
113
116
  pathDistance: number
114
117
  label: NetLabelPlacement
@@ -117,6 +120,7 @@ const createCandidate = (params: {
117
120
  point,
118
121
  orientation,
119
122
  netLabelWidth,
123
+ netLabelHeight,
120
124
  traceLocation,
121
125
  pathDistance,
122
126
  label,
@@ -124,6 +128,7 @@ const createCandidate = (params: {
124
128
  const { width, height } = getDimsForOrientation({
125
129
  orientation,
126
130
  netLabelWidth,
131
+ netLabelHeight,
127
132
  })
128
133
 
129
134
  return {
@@ -360,6 +365,20 @@ const getNetLabelWidth = (
360
365
  return label.width
361
366
  }
362
367
 
368
+ const getNetLabelHeight = (
369
+ inputProblem: InputProblem,
370
+ label: NetLabelPlacement,
371
+ ): number | undefined => {
372
+ const ncHeightByNetId = inputProblem.netConnections.find(
373
+ (connection) => connection.netId === label.netId,
374
+ )?.netLabelHeight
375
+ if (ncHeightByNetId !== undefined) return ncHeightByNetId
376
+
377
+ return inputProblem.netConnections.find((nc) =>
378
+ nc.pinIds.some((pid) => label.pinIds.includes(pid)),
379
+ )?.netLabelHeight
380
+ }
381
+
363
382
  const roundDistance = (distance: number) => Number(distance.toFixed(6))
364
383
 
365
384
  const dedupeOrientations = (orientations: FacingDirection[]) => [
@@ -31,6 +31,7 @@ export interface InputNetConnection {
31
31
  netId: string
32
32
  pinIds: Array<PinId>
33
33
  netLabelWidth?: number
34
+ netLabelHeight?: number
34
35
  }
35
36
 
36
37
  export interface InputProblem {
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.62",
4
+ "version": "0.0.64",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "start": "cosmos",
@@ -65,7 +65,8 @@
65
65
  {
66
66
  "netId": "SWD",
67
67
  "pinIds": ["U3.25", "R5.2"],
68
- "netLabelWidth": 0.48
68
+ "netLabelWidth": 0.48,
69
+ "netLabelHeight": 0.48
69
70
  },
70
71
  {
71
72
  "netId": "CLK",
@@ -2,56 +2,60 @@
2
2
  <rect width="100%" height="100%" fill="white" />
3
3
  <g>
4
4
  <circle data-type="point" data-label="U1.1
5
- x+" data-x="1.1" data-y="0.30000000000000004" cx="487.83715012722644" cy="248.75318066157763" r="3" fill="hsl(319, 100%, 50%, 0.8)" />
5
+ x+" data-x="1.1" data-y="0.30000000000000004" cx="479.84291324526953" cy="250.0249910746162" r="3" fill="hsl(319, 100%, 50%, 0.8)" />
6
6
  </g>
7
7
  <g>
8
8
  <circle data-type="point" data-label="U1.2
9
- x+" data-x="1.1" data-y="0.09999999999999998" cx="487.83715012722644" cy="289.46564885496184" r="3" fill="hsl(320, 100%, 50%, 0.8)" />
9
+ x+" data-x="1.1" data-y="0.09999999999999998" cx="479.84291324526953" cy="290.0107104605498" r="3" fill="hsl(320, 100%, 50%, 0.8)" />
10
10
  </g>
11
11
  <g>
12
12
  <circle data-type="point" data-label="U1.3
13
- x+" data-x="1.1" data-y="-0.10000000000000003" cx="487.83715012722644" cy="330.17811704834605" r="3" fill="hsl(321, 100%, 50%, 0.8)" />
13
+ x+" data-x="1.1" data-y="-0.10000000000000003" cx="479.84291324526953" cy="329.9964298464834" r="3" fill="hsl(321, 100%, 50%, 0.8)" />
14
14
  </g>
15
15
  <g>
16
16
  <circle data-type="point" data-label="U1.4
17
- x+" data-x="1.1" data-y="-0.30000000000000004" cx="487.83715012722644" cy="370.89058524173026" r="3" fill="hsl(322, 100%, 50%, 0.8)" />
17
+ x+" data-x="1.1" data-y="-0.30000000000000004" cx="479.84291324526953" cy="369.982149232417" r="3" fill="hsl(322, 100%, 50%, 0.8)" />
18
18
  </g>
19
19
  <g>
20
- <circle data-type="point" data-label="" data-x="1.1" data-y="0.30000000000000004" cx="487.83715012722644" cy="248.75318066157763" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
20
+ <circle data-type="point" data-label="anchorPoint
21
+ orientation: x+" data-x="1.1" data-y="0.30000000000000004" cx="479.84291324526953" cy="250.0249910746162" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
21
22
  </g>
22
23
  <g>
23
- <circle data-type="point" data-label="" data-x="1.1" data-y="0.09999999999999998" cx="487.83715012722644" cy="289.46564885496184" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
24
+ <circle data-type="point" data-label="anchorPoint
25
+ orientation: x+" data-x="1.1" data-y="0.09999999999999998" cx="479.84291324526953" cy="290.0107104605498" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
24
26
  </g>
25
27
  <g>
26
- <circle data-type="point" data-label="" data-x="1.5510000000000002" data-y="0.4009999999999999" cx="579.6437659033079" cy="228.1933842239186" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
28
+ <circle data-type="point" data-label="anchorPoint
29
+ orientation: y+" data-x="1.601" data-y="0.4009999999999999" cx="580.0071403070332" cy="229.83220278471975" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
27
30
  </g>
28
31
  <g>
29
- <circle data-type="point" data-label="" data-x="1.201" data-y="-0.5010000000000001" cx="508.39694656488547" cy="411.80661577608146" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
32
+ <circle data-type="point" data-label="anchorPoint
33
+ orientation: y-" data-x="1.201" data-y="-0.5010000000000001" cx="500.035701535166" cy="410.1677972152803" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
30
34
  </g>
31
35
  <g>
32
- <polyline data-points="1.1,-0.10000000000000003 1.5510000000000002,-0.10000000000000003 1.5510000000000002,0.4009999999999999" data-type="line" data-label="" points="487.83715012722644,330.17811704834605 579.6437659033079,330.17811704834605 579.6437659033079,228.1933842239186" fill="none" stroke="purple" stroke-width="1" />
36
+ <polyline data-points="1.1,-0.10000000000000003 1.601,-0.10000000000000003 1.601,0.4009999999999999" data-type="line" data-label="" points="479.84291324526953,329.9964298464834 580.0071403070332,329.9964298464834 580.0071403070332,229.83220278471975" fill="none" stroke="purple" stroke-width="1" />
33
37
  </g>
34
38
  <g>
35
- <polyline data-points="1.1,-0.30000000000000004 1.201,-0.30000000000000004 1.201,-0.5010000000000001" data-type="line" data-label="" points="487.83715012722644,370.89058524173026 508.39694656488547,370.89058524173026 508.39694656488547,411.80661577608146" fill="none" stroke="purple" stroke-width="1" />
39
+ <polyline data-points="1.1,-0.30000000000000004 1.201,-0.30000000000000004 1.201,-0.5010000000000001" data-type="line" data-label="" points="479.84291324526953,369.982149232417 500.035701535166,369.982149232417 500.035701535166,410.1677972152803" fill="none" stroke="purple" stroke-width="1" />
36
40
  </g>
37
41
  <g>
38
- <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="40" y="208.04071246819342" width="447.83715012722644" height="203.56234096692106" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.004912500000000001" />
42
+ <rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="39.99999999999997" y="210.03927168868262" width="439.84291324526953" height="199.92859692966795" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.005001785714285715" />
39
43
  </g>
40
44
  <g>
41
45
  <rect data-type="rect" data-label="netId: SCL
42
- globalConnNetId: connectivity_net0" data-x="1.326" data-y="0.30000000000000004" x="488.0407124681933" y="228.39694656488552" width="91.60305343511459" height="40.71246819338421" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.004912500000000001" />
46
+ globalConnNetId: connectivity_net0" data-x="1.326" data-y="0.30000000000000004" x="480.0428418421992" y="230.03213138164938" width="89.96786861835062" height="39.985719385933635" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.005001785714285715" />
43
47
  </g>
44
48
  <g>
45
49
  <rect data-type="rect" data-label="netId: SDA
46
- globalConnNetId: connectivity_net1" data-x="1.326" data-y="0.09999999999999998" x="488.0407124681933" y="269.10941475826974" width="91.60305343511459" height="40.71246819338421" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.004912500000000001" />
50
+ globalConnNetId: connectivity_net1" data-x="1.326" data-y="0.09999999999999998" x="480.0428418421992" y="270.017850767583" width="89.96786861835062" height="39.98571938593358" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.005001785714285715" />
47
51
  </g>
48
52
  <g>
49
53
  <rect data-type="rect" data-label="netId: V3_3
50
- globalConnNetId: connectivity_net2" data-x="1.5510000000000002" data-y="0.6259999999999999" x="559.2875318066158" y="136.59033078880412" width="40.71246819338421" height="91.60305343511448" fill="#ef444466" stroke="#ef4444" stroke-width="0.004912500000000001" />
54
+ globalConnNetId: connectivity_net2" data-x="1.601" data-y="0.6259999999999999" x="560.0142806140664" y="139.86433416636916" width="39.985719385933635" height="89.9678686183506" fill="#ef444466" stroke="#ef4444" stroke-width="0.005001785714285715" />
51
55
  </g>
52
56
  <g>
53
57
  <rect data-type="rect" data-label="netId: GND
54
- globalConnNetId: connectivity_net3" data-x="1.201" data-y="-0.7260000000000001" x="488.0407124681933" y="411.80661577608146" width="40.71246819338421" height="91.60305343511448" fill="#00000066" stroke="#000000" stroke-width="0.004912500000000001" />
58
+ globalConnNetId: connectivity_net3" data-x="1.201" data-y="-0.7260000000000001" x="480.0428418421992" y="410.1677972152803" width="39.985719385933635" height="89.96786861835051" fill="#00000066" stroke="#000000" stroke-width="0.005001785714285715" />
55
59
  </g>
56
60
  <g id="crosshair" style="display: none">
57
61
  <line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
@@ -81,12 +85,12 @@ globalConnNetId: connectivity_net3" data-x="1.201" data-y="-0.7260000000000001"
81
85
 
82
86
  // Calculate real coordinates using inverse transformation
83
87
  const matrix = {
84
- "a": 203.56234096692108,
88
+ "a": 199.92859692966798,
85
89
  "c": 0,
86
- "e": 263.9185750636132,
90
+ "e": 259.92145662263476,
87
91
  "b": 0,
88
- "d": -203.56234096692108,
89
- "f": 309.82188295165395
92
+ "d": -199.92859692966798,
93
+ "f": 310.0035701535166
90
94
  };
91
95
  // Manually invert and apply the affine transform
92
96
  // Since we only use translate and scale, we can directly compute:
@@ -26,7 +26,7 @@ orientation: x-" data-x="-7.65" data-y="-0.22999999999999998" cx="325.9574468085
26
26
  </g>
27
27
  <g>
28
28
  <circle data-type="point" data-label="anchorPoint
29
- orientation: y+" data-x="-8.549999999999999" data-y="-0.4299999999999998" cx="218.72340425531945" cy="331.9148936170212" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
29
+ orientation: y+" data-x="-8.65" data-y="-0.4299999999999998" cx="206.808510638298" cy="331.9148936170212" r="3" fill="hsl(40, 100%, 50%, 0.9)" />
30
30
  </g>
31
31
  <g>
32
32
  <circle data-type="point" data-label="anchorPoint
@@ -53,7 +53,7 @@ globalConnNetId: connectivity_net1" data-x="-8.011" data-y="-0.22999999999999998
53
53
  </g>
54
54
  <g>
55
55
  <rect data-type="rect" data-label="netId: SWD
56
- globalConnNetId: connectivity_net0" data-x="-8.549999999999999" data-y="-0.18989999999999985" x="206.80851063829823" y="274.7114893617021" width="23.829787234042442" height="57.19148936170211" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008392857142857145" />
56
+ globalConnNetId: connectivity_net0" data-x="-8.65" data-y="-0.18989999999999985" x="178.2127659574469" y="274.7114893617021" width="57.191489361702224" height="57.19148936170211" fill="hsl(40, 100%, 50%, 0.35)" stroke="black" stroke-width="0.008392857142857145" />
57
57
  </g>
58
58
  <g>
59
59
  <rect data-type="rect" data-label="netId: CLK
@@ -2,11 +2,19 @@ import { test, expect } from "bun:test"
2
2
  import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
3
3
  import inputProblem from "../assets/example34.json"
4
4
  import "tests/fixtures/matcher"
5
+ import { getTraceLabelCollisions } from "tests/fixtures/traceLabelCollisions"
5
6
 
6
7
  test("example34", () => {
7
8
  const solver = new SchematicTracePipelineSolver(inputProblem as any)
8
9
 
9
10
  solver.solve()
10
11
 
12
+ expect(
13
+ getTraceLabelCollisions(
14
+ solver.netLabelNetLabelCollisionSolver!.traces,
15
+ solver.netLabelNetLabelCollisionSolver!.outputNetLabelPlacements,
16
+ ),
17
+ ).toEqual([])
18
+
11
19
  expect(solver).toMatchSolverSnapshot(import.meta.path)
12
20
  })
@@ -0,0 +1,42 @@
1
+ import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
2
+ import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
3
+ import { segmentIntersectsRect } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
4
+ import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
5
+
6
+ export const getTraceLabelCollisions = (
7
+ traces: SolvedTracePath[],
8
+ labels: NetLabelPlacement[],
9
+ ) => {
10
+ const collisions: Array<{
11
+ traceId: string
12
+ traceNetId: string
13
+ labelNetId: string
14
+ segmentIndex: number
15
+ }> = []
16
+
17
+ for (const trace of traces) {
18
+ for (const label of labels) {
19
+ if (trace.globalConnNetId === label.globalConnNetId) continue
20
+
21
+ const bounds = getRectBounds(label.center, label.width, label.height)
22
+ for (let i = 0; i < trace.tracePath.length - 1; i++) {
23
+ if (
24
+ segmentIntersectsRect(
25
+ trace.tracePath[i]!,
26
+ trace.tracePath[i + 1]!,
27
+ bounds,
28
+ )
29
+ ) {
30
+ collisions.push({
31
+ traceId: trace.mspPairId,
32
+ traceNetId: trace.globalConnNetId,
33
+ labelNetId: label.globalConnNetId,
34
+ segmentIndex: i,
35
+ })
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ return collisions
42
+ }