@tscircuit/schematic-trace-solver 0.0.63 → 0.0.65
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 +5 -0
- package/dist/index.js +208 -143
- package/lib/solvers/AvailableNetOrientationSolver/AvailableNetOrientationSolver.ts +51 -36
- package/lib/solvers/AvailableNetOrientationSolver/geometry.ts +13 -0
- package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +9 -13
- package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts +89 -6
- package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapShiftSolver.ts +1 -0
- package/package.json +1 -1
- package/tests/examples/__snapshots__/example34.snap.svg +23 -19
- 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
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
rectsOverlap,
|
|
23
23
|
simplifyOrthogonalPath,
|
|
24
24
|
traceCrossesBoundsInterior,
|
|
25
|
+
tracePathIntersectsBounds,
|
|
25
26
|
tracePathCrossesAnyBounds,
|
|
26
27
|
tracePathCrossesAnyTrace,
|
|
27
28
|
} from "./geometry"
|
|
@@ -143,30 +144,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
143
144
|
candidate: EvaluatedCandidate,
|
|
144
145
|
labelIndex: number,
|
|
145
146
|
) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (candidate.phase === "lateral-shift") {
|
|
149
|
-
const orientDir = dir(candidate.orientation)
|
|
150
|
-
const kickedSource = {
|
|
151
|
-
x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
|
|
152
|
-
y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP,
|
|
153
|
-
}
|
|
154
|
-
tracePath = simplifyOrthogonalPath([
|
|
155
|
-
label.anchorPoint,
|
|
156
|
-
...getConnectorTracePath(
|
|
157
|
-
kickedSource,
|
|
158
|
-
candidate.anchorPoint,
|
|
159
|
-
candidate.orientation,
|
|
160
|
-
),
|
|
161
|
-
])
|
|
162
|
-
} else {
|
|
163
|
-
tracePath = getConnectorTracePath(
|
|
164
|
-
label.anchorPoint,
|
|
165
|
-
candidate.anchorPoint,
|
|
166
|
-
candidate.orientation,
|
|
167
|
-
)
|
|
168
|
-
}
|
|
169
|
-
|
|
147
|
+
const tracePath = this.getCandidateConnectorTrace(label, candidate)
|
|
170
148
|
if (tracePath.length < 2) return
|
|
171
149
|
|
|
172
150
|
const mspPairId = `available-net-orientation-${labelIndex}-${label.netId ?? label.globalConnNetId}`
|
|
@@ -417,8 +395,43 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
417
395
|
distance,
|
|
418
396
|
outwardDistance,
|
|
419
397
|
selected: false,
|
|
420
|
-
status: this.getCandidateStatus(
|
|
398
|
+
status: this.getCandidateStatus({
|
|
399
|
+
candidate,
|
|
400
|
+
label,
|
|
401
|
+
labelIndex,
|
|
402
|
+
phase,
|
|
403
|
+
}),
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
private getCandidateConnectorTrace(
|
|
408
|
+
label: NetLabelPlacement,
|
|
409
|
+
candidate: Pick<
|
|
410
|
+
EvaluatedCandidate,
|
|
411
|
+
"anchorPoint" | "orientation" | "phase"
|
|
412
|
+
>,
|
|
413
|
+
) {
|
|
414
|
+
if (candidate.phase === "lateral-shift") {
|
|
415
|
+
const orientDir = dir(candidate.orientation)
|
|
416
|
+
const kickedSource = {
|
|
417
|
+
x: label.anchorPoint.x - orientDir.x * LABEL_SEARCH_STEP,
|
|
418
|
+
y: label.anchorPoint.y - orientDir.y * LABEL_SEARCH_STEP,
|
|
419
|
+
}
|
|
420
|
+
return simplifyOrthogonalPath([
|
|
421
|
+
label.anchorPoint,
|
|
422
|
+
...getConnectorTracePath(
|
|
423
|
+
kickedSource,
|
|
424
|
+
candidate.anchorPoint,
|
|
425
|
+
candidate.orientation,
|
|
426
|
+
),
|
|
427
|
+
])
|
|
421
428
|
}
|
|
429
|
+
|
|
430
|
+
return getConnectorTracePath(
|
|
431
|
+
label.anchorPoint,
|
|
432
|
+
candidate.anchorPoint,
|
|
433
|
+
candidate.orientation,
|
|
434
|
+
)
|
|
422
435
|
}
|
|
423
436
|
|
|
424
437
|
private getSearchStartAnchor(
|
|
@@ -583,22 +596,24 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
583
596
|
)?.netLabelHeight
|
|
584
597
|
}
|
|
585
598
|
|
|
586
|
-
private getCandidateStatus(
|
|
587
|
-
candidate: CandidateLabel
|
|
588
|
-
label: NetLabelPlacement
|
|
589
|
-
labelIndex: number
|
|
590
|
-
|
|
599
|
+
private getCandidateStatus(params: {
|
|
600
|
+
candidate: CandidateLabel
|
|
601
|
+
label: NetLabelPlacement
|
|
602
|
+
labelIndex: number
|
|
603
|
+
phase: CandidatePhase
|
|
604
|
+
}) {
|
|
605
|
+
const { candidate, label, labelIndex, phase } = params
|
|
591
606
|
const boundsStatus = this.getBoundsStatus(
|
|
592
607
|
getRectBounds(candidate.center, candidate.width, candidate.height),
|
|
593
608
|
labelIndex,
|
|
594
609
|
)
|
|
595
610
|
if (boundsStatus !== "valid") return boundsStatus
|
|
596
611
|
|
|
597
|
-
const connectorTrace =
|
|
598
|
-
|
|
599
|
-
candidate.
|
|
600
|
-
|
|
601
|
-
)
|
|
612
|
+
const connectorTrace = this.getCandidateConnectorTrace(label, {
|
|
613
|
+
anchorPoint: candidate.anchorPoint,
|
|
614
|
+
orientation: candidate.orientation,
|
|
615
|
+
phase,
|
|
616
|
+
})
|
|
602
617
|
|
|
603
618
|
if (tracePathCrossesAnyTrace(connectorTrace, this.traceMap)) {
|
|
604
619
|
return "trace-collision"
|
|
@@ -614,7 +629,7 @@ export class AvailableNetOrientationSolver extends BaseSolver {
|
|
|
614
629
|
if (i === labelIndex) continue
|
|
615
630
|
const otherLabel = this.outputNetLabelPlacements[i]!
|
|
616
631
|
if (
|
|
617
|
-
|
|
632
|
+
tracePathIntersectsBounds(
|
|
618
633
|
connectorTrace,
|
|
619
634
|
getRectBounds(otherLabel.center, otherLabel.width, otherLabel.height),
|
|
620
635
|
)
|
|
@@ -2,6 +2,7 @@ import type { Point } from "@tscircuit/math-utils"
|
|
|
2
2
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
3
3
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
4
4
|
import type { FacingDirection } from "lib/utils/dir"
|
|
5
|
+
import { segmentIntersectsRect } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
|
|
5
6
|
import { EPS, TRACE_BOUNDARY_TOLERANCE } from "./constants"
|
|
6
7
|
import type { Bounds, ChipSide } from "./types"
|
|
7
8
|
|
|
@@ -102,6 +103,18 @@ export const tracePathCrossesAnyBounds = (
|
|
|
102
103
|
return false
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
export const tracePathIntersectsBounds = (
|
|
107
|
+
tracePath: Point[],
|
|
108
|
+
bounds: Bounds,
|
|
109
|
+
) => {
|
|
110
|
+
for (let i = 0; i < tracePath.length - 1; i++) {
|
|
111
|
+
if (segmentIntersectsRect(tracePath[i]!, tracePath[i + 1]!, bounds)) {
|
|
112
|
+
return true
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return false
|
|
116
|
+
}
|
|
117
|
+
|
|
105
118
|
export const tracePathCrossesAnyTrace = (
|
|
106
119
|
tracePath: Point[],
|
|
107
120
|
traceMap: Record<string, SolvedTracePath>,
|
|
@@ -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
|
)
|
package/lib/solvers/TraceOverlapShiftSolver/TraceOverlapIssueSolver/TraceOverlapIssueSolver.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { GraphicsObject } from "graphics-debug"
|
|
2
2
|
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
3
3
|
import type { MspConnectionPairId } from "lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver"
|
|
4
|
+
import { findFirstCollision } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
5
|
+
import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
4
6
|
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
7
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
5
8
|
import { applyJogToTerminalSegment } from "./applyJogToTrace"
|
|
6
9
|
|
|
7
10
|
type ConnNetId = string
|
|
8
11
|
|
|
12
|
+
const EPS = 1e-6
|
|
13
|
+
|
|
9
14
|
export interface OverlappingTraceSegmentLocator {
|
|
10
15
|
connNetId: string
|
|
11
16
|
pathsWithOverlap: Array<{
|
|
@@ -17,18 +22,21 @@ export interface OverlappingTraceSegmentLocator {
|
|
|
17
22
|
export class TraceOverlapIssueSolver extends BaseSolver {
|
|
18
23
|
overlappingTraceSegments: OverlappingTraceSegmentLocator[]
|
|
19
24
|
traceNetIslands: Record<ConnNetId, Array<SolvedTracePath>>
|
|
25
|
+
obstacleRects: ReturnType<typeof getObstacleRects>
|
|
20
26
|
|
|
21
27
|
SHIFT_DISTANCE = 0.1
|
|
22
28
|
|
|
23
29
|
correctedTraceMap: Record<MspConnectionPairId, SolvedTracePath> = {}
|
|
24
30
|
|
|
25
31
|
constructor(params: {
|
|
32
|
+
inputProblem: InputProblem
|
|
26
33
|
overlappingTraceSegments: OverlappingTraceSegmentLocator[]
|
|
27
34
|
traceNetIslands: Record<ConnNetId, Array<SolvedTracePath>>
|
|
28
35
|
}) {
|
|
29
36
|
super()
|
|
30
37
|
this.overlappingTraceSegments = params.overlappingTraceSegments
|
|
31
38
|
this.traceNetIslands = params.traceNetIslands
|
|
39
|
+
this.obstacleRects = getObstacleRects(params.inputProblem)
|
|
32
40
|
|
|
33
41
|
// Only add the relevant traces to the correctedTraceMap
|
|
34
42
|
for (const { connNetId, pathsWithOverlap } of this
|
|
@@ -49,13 +57,14 @@ export class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
49
57
|
// Shift only the overlapping segments, and move the shared endpoints
|
|
50
58
|
// (the last point of the previous segment and the first point of the next
|
|
51
59
|
// segment) so the polyline remains orthogonal without self-overlap.
|
|
52
|
-
const EPS = 1e-6
|
|
53
|
-
|
|
54
60
|
// Compute offsets for each island involved: alternate directions
|
|
55
|
-
const offsets = this.overlappingTraceSegments.map((
|
|
61
|
+
const offsets = this.overlappingTraceSegments.map((group, idx) => {
|
|
56
62
|
const n = Math.floor(idx / 2) + 1
|
|
57
63
|
const signed = idx % 2 === 0 ? -n : n
|
|
58
|
-
return
|
|
64
|
+
return this.getObstacleAwareOffset({
|
|
65
|
+
group,
|
|
66
|
+
offset: signed * this.SHIFT_DISTANCE,
|
|
67
|
+
})
|
|
59
68
|
})
|
|
60
69
|
|
|
61
70
|
const eq = (a: number, b: number) => Math.abs(a - b) < EPS
|
|
@@ -82,8 +91,6 @@ export class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
82
91
|
const current = this.correctedTraceMap[original.mspPairId] ?? original
|
|
83
92
|
const pts = current.tracePath.map((p) => ({ ...p }))
|
|
84
93
|
|
|
85
|
-
const segIdxs = Array.from(segIdxSet).sort((a, b) => a - b)
|
|
86
|
-
|
|
87
94
|
const segIdxsRev = Array.from(segIdxSet)
|
|
88
95
|
.sort((a, b) => a - b)
|
|
89
96
|
.reverse()
|
|
@@ -142,6 +149,82 @@ export class TraceOverlapIssueSolver extends BaseSolver {
|
|
|
142
149
|
this.solved = true
|
|
143
150
|
}
|
|
144
151
|
|
|
152
|
+
private getObstacleAwareOffset({
|
|
153
|
+
group,
|
|
154
|
+
offset,
|
|
155
|
+
}: {
|
|
156
|
+
group: OverlappingTraceSegmentLocator
|
|
157
|
+
offset: number
|
|
158
|
+
}) {
|
|
159
|
+
const blockingCollisions = this.getBlockingCollisions({ group, offset })
|
|
160
|
+
if (blockingCollisions.length === 0) return offset
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
blockingCollisions
|
|
164
|
+
.flatMap(({ start, isVertical, obstacle }) => [
|
|
165
|
+
(isVertical ? obstacle.minX - start.x : obstacle.minY - start.y) -
|
|
166
|
+
this.SHIFT_DISTANCE,
|
|
167
|
+
(isVertical ? obstacle.maxX - start.x : obstacle.maxY - start.y) +
|
|
168
|
+
this.SHIFT_DISTANCE,
|
|
169
|
+
])
|
|
170
|
+
.filter(
|
|
171
|
+
(candidateOffset) =>
|
|
172
|
+
this.getBlockingCollisions({ group, offset: candidateOffset })
|
|
173
|
+
.length === 0,
|
|
174
|
+
)
|
|
175
|
+
.sort((a, b) => Math.abs(a - offset) - Math.abs(b - offset))[0] ??
|
|
176
|
+
offset
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private getBlockingCollisions({
|
|
181
|
+
group,
|
|
182
|
+
offset,
|
|
183
|
+
}: {
|
|
184
|
+
group: OverlappingTraceSegmentLocator
|
|
185
|
+
offset: number
|
|
186
|
+
}) {
|
|
187
|
+
const blockingCollisions: Array<{
|
|
188
|
+
start: SolvedTracePath["tracePath"][number]
|
|
189
|
+
isVertical: boolean
|
|
190
|
+
obstacle: ReturnType<typeof getObstacleRects>[number]
|
|
191
|
+
}> = []
|
|
192
|
+
|
|
193
|
+
for (const {
|
|
194
|
+
solvedTracePathIndex,
|
|
195
|
+
traceSegmentIndex,
|
|
196
|
+
} of group.pathsWithOverlap) {
|
|
197
|
+
const trace = this.traceNetIslands[group.connNetId][solvedTracePathIndex]!
|
|
198
|
+
const start = trace.tracePath[traceSegmentIndex]
|
|
199
|
+
const end = trace.tracePath[traceSegmentIndex + 1]
|
|
200
|
+
if (!start || !end) continue
|
|
201
|
+
|
|
202
|
+
const isVertical = Math.abs(start.x - end.x) < EPS
|
|
203
|
+
const isHorizontal = Math.abs(start.y - end.y) < EPS
|
|
204
|
+
if (!isVertical && !isHorizontal) continue
|
|
205
|
+
|
|
206
|
+
const shiftedSegment = isVertical
|
|
207
|
+
? [
|
|
208
|
+
{ ...start, x: start.x + offset },
|
|
209
|
+
{ ...end, x: end.x + offset },
|
|
210
|
+
]
|
|
211
|
+
: [
|
|
212
|
+
{ ...start, y: start.y + offset },
|
|
213
|
+
{ ...end, y: end.y + offset },
|
|
214
|
+
]
|
|
215
|
+
const collision = findFirstCollision(shiftedSegment, this.obstacleRects)
|
|
216
|
+
if (collision) {
|
|
217
|
+
blockingCollisions.push({
|
|
218
|
+
start,
|
|
219
|
+
isVertical,
|
|
220
|
+
obstacle: collision.rect,
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return blockingCollisions
|
|
226
|
+
}
|
|
227
|
+
|
|
145
228
|
override visualize(): GraphicsObject {
|
|
146
229
|
// Visualize overlapped segments and proposed corrections
|
|
147
230
|
const graphics: GraphicsObject = {
|
|
@@ -309,6 +309,7 @@ export class TraceOverlapShiftSolver extends BaseSolver {
|
|
|
309
309
|
const { overlappingTraceSegments } = overlapIssue
|
|
310
310
|
|
|
311
311
|
this.activeSubSolver = new TraceOverlapIssueSolver({
|
|
312
|
+
inputProblem: this.inputProblem,
|
|
312
313
|
overlappingTraceSegments,
|
|
313
314
|
traceNetIslands: this.traceNetIslands,
|
|
314
315
|
})
|
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:
|
|
@@ -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
|
+
}
|