@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.
- package/dist/index.d.ts +7 -0
- package/dist/index.js +126 -52
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +67 -36
- package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +13 -0
- package/lib/solvers/NetLabelNetLabelCollisionSolver/NetLabelNetLabelCollisionSolver.ts +8 -0
- package/lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver.ts +24 -0
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/SingleNetLabelPlacementSolver.ts +7 -1
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry.ts +9 -4
- package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts +3 -0
- package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +9 -13
- package/lib/solvers/TraceAnchoredNetLabelOverlapSolver/candidates.ts +19 -0
- package/lib/types/InputProblem.ts +1 -0
- package/package.json +1 -1
- package/tests/assets/example41.json +2 -1
- package/tests/examples/__snapshots__/example34.snap.svg +23 -19
- package/tests/examples/__snapshots__/example41.snap.svg +2 -2
- package/tests/examples/example34.test.ts +8 -0
- package/tests/fixtures/traceLabelCollisions.ts +42 -0
- package/tests/repros/__snapshots__/netlabel-connector-through-rail-label.snap.svg +273 -0
- package/tests/repros/__snapshots__/trace-overlap-box-resistor.snap.svg +113 -0
- package/tests/repros/assets/repro-netlabel-connector-through-label.input.json +139 -0
- package/tests/repros/assets/repro126-trace-overlap-box-resistor.input.json +74 -0
- package/tests/repros/netlabel-connector-through-rail-label.test.ts +19 -0
- 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,
|
|
19
|
-
width:
|
|
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:
|
|
30
|
+
height: horizHeight,
|
|
26
31
|
}
|
|
27
32
|
}
|
|
28
33
|
|
package/lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/solvePortOnlyPin.ts
CHANGED
|
@@ -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
|
|
75
|
+
interface TraceLabelOverlap {
|
|
76
76
|
trace: SolvedTracePath
|
|
77
77
|
label: NetLabelPlacement
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Detects overlaps where a trace
|
|
82
|
-
*
|
|
83
|
-
*
|
|
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
|
|
85
|
+
function detectTraceLabelOverlaps(
|
|
86
86
|
traces: SolvedTracePath[],
|
|
87
87
|
labels: NetLabelPlacement[],
|
|
88
|
-
):
|
|
89
|
-
const overlaps:
|
|
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
|
-
|
|
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 =
|
|
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[]) => [
|
package/package.json
CHANGED
|
@@ -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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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.
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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.
|
|
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="
|
|
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":
|
|
88
|
+
"a": 199.92859692966798,
|
|
85
89
|
"c": 0,
|
|
86
|
-
"e":
|
|
90
|
+
"e": 259.92145662263476,
|
|
87
91
|
"b": 0,
|
|
88
|
-
"d": -
|
|
89
|
-
"f":
|
|
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.
|
|
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.
|
|
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
|
+
}
|