@tscircuit/schematic-trace-solver 0.0.42 → 0.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +28 -29
- package/dist/index.js +917 -926
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +20 -1
- package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +133 -0
- package/lib/solvers/TraceCleanupSolver/balanceZShapes.ts +200 -0
- package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +76 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +1 -21
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +1 -1
- package/package.json +1 -1
- package/site/examples/example26.page.tsx +4 -0
- package/tests/assets/example26.json +1206 -0
- package/tests/examples/__snapshots__/example02.snap.svg +1 -1
- package/tests/examples/__snapshots__/example09.snap.svg +34 -34
- package/tests/examples/__snapshots__/example11.snap.svg +13 -13
- package/tests/examples/__snapshots__/example12.snap.svg +3 -3
- package/tests/examples/__snapshots__/example13.snap.svg +12 -12
- package/tests/examples/__snapshots__/example16.snap.svg +4 -4
- package/tests/examples/__snapshots__/example19.snap.svg +9 -9
- package/tests/examples/__snapshots__/example25.snap.svg +3 -3
- package/tests/examples/__snapshots__/example26.snap.svg +3 -3
- package/tests/examples/__snapshots__/example28.snap.svg +3 -3
- package/tests/examples/__snapshots__/example29.snap.svg +1208 -0
- package/tests/examples/example29.test.ts +13 -0
- package/tests/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solver → TraceCleanupSolver}/TraceCleanupSolver.test.ts +2 -2
- package/tests/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solver → TraceCleanupSolver}/__snapshots__/TraceCleanupSolver.snap.svg +1 -1
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView03.snap.svg +1 -1
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts +0 -132
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/balanceLShapes.ts +0 -209
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +0 -66
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/countTurns.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/hasCollisions.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/hasCollisionsWithLabels.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/simplifyPath.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/tryConnectPoints.ts +0 -0
- /package/lib/solvers/{TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver → TraceCleanupSolver}/turnMinimization.ts +0 -0
|
@@ -19,7 +19,7 @@ import { correctPinsInsideChips } from "./correctPinsInsideChip"
|
|
|
19
19
|
import { expandChipsToFitPins } from "./expandChipsToFitPins"
|
|
20
20
|
import { LongDistancePairSolver } from "../LongDistancePairSolver/LongDistancePairSolver"
|
|
21
21
|
import { MergedNetLabelObstacleSolver } from "../TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver"
|
|
22
|
-
import { TraceCleanupSolver } from "../
|
|
22
|
+
import { TraceCleanupSolver } from "../TraceCleanupSolver/TraceCleanupSolver"
|
|
23
23
|
|
|
24
24
|
type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
|
|
25
25
|
solverName: string
|
|
@@ -188,6 +188,25 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
188
188
|
]
|
|
189
189
|
},
|
|
190
190
|
),
|
|
191
|
+
definePipelineStep("traceCleanupSolver", TraceCleanupSolver, (instance) => {
|
|
192
|
+
const prevSolverOutput =
|
|
193
|
+
instance.traceLabelOverlapAvoidanceSolver!.getOutput()
|
|
194
|
+
const traces = prevSolverOutput.traces
|
|
195
|
+
|
|
196
|
+
const labelMergingOutput =
|
|
197
|
+
instance.traceLabelOverlapAvoidanceSolver!.labelMergingSolver!.getOutput()
|
|
198
|
+
|
|
199
|
+
return [
|
|
200
|
+
{
|
|
201
|
+
inputProblem: instance.inputProblem,
|
|
202
|
+
allTraces: traces,
|
|
203
|
+
targetTraceIds: new Set(traces.map((t) => t.mspPairId)),
|
|
204
|
+
allLabelPlacements: labelMergingOutput.netLabelPlacements,
|
|
205
|
+
mergedLabelNetIdMap: labelMergingOutput.mergedLabelNetIdMap,
|
|
206
|
+
paddingBuffer: 0.1,
|
|
207
|
+
},
|
|
208
|
+
]
|
|
209
|
+
}),
|
|
191
210
|
definePipelineStep(
|
|
192
211
|
"netLabelPlacementSolver",
|
|
193
212
|
NetLabelPlacementSolver,
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
2
|
+
import type { GraphicsObject, Line } from "graphics-debug"
|
|
3
|
+
import { minimizeTurnsWithFilteredLabels } from "./minimizeTurnsWithFilteredLabels"
|
|
4
|
+
import { balanceZShapes } from "./balanceZShapes"
|
|
5
|
+
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
6
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
7
|
+
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
8
|
+
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
9
|
+
|
|
10
|
+
interface TraceCleanupSolverInput {
|
|
11
|
+
inputProblem: InputProblem
|
|
12
|
+
allTraces: SolvedTracePath[]
|
|
13
|
+
targetTraceIds: Set<string>
|
|
14
|
+
allLabelPlacements: NetLabelPlacement[]
|
|
15
|
+
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
16
|
+
paddingBuffer: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type PipelineStep = "minimizing_turns" | "balancing_l_shapes"
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Cleans up traces by minimizing turns and balancing L-shapes to improve
|
|
23
|
+
* the overall aesthetics and readability of the schematic.
|
|
24
|
+
*/
|
|
25
|
+
export class TraceCleanupSolver extends BaseSolver {
|
|
26
|
+
private input: TraceCleanupSolverInput
|
|
27
|
+
private outputTraces: SolvedTracePath[]
|
|
28
|
+
private traceIdQueue: string[]
|
|
29
|
+
private tracesMap: Map<string, SolvedTracePath>
|
|
30
|
+
private pipelineStep: PipelineStep = "minimizing_turns"
|
|
31
|
+
private activeTraceId: string | null = null // New property
|
|
32
|
+
|
|
33
|
+
constructor(solverInput: TraceCleanupSolverInput) {
|
|
34
|
+
super()
|
|
35
|
+
this.input = solverInput
|
|
36
|
+
this.outputTraces = [...solverInput.allTraces]
|
|
37
|
+
this.tracesMap = new Map(this.outputTraces.map((t) => [t.mspPairId, t]))
|
|
38
|
+
this.traceIdQueue = Array.from(solverInput.targetTraceIds)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
override _step() {
|
|
42
|
+
if (
|
|
43
|
+
this.pipelineStep === "minimizing_turns" &&
|
|
44
|
+
this.traceIdQueue.length === 0
|
|
45
|
+
) {
|
|
46
|
+
this.pipelineStep = "balancing_l_shapes"
|
|
47
|
+
this.traceIdQueue = Array.from(this.input.targetTraceIds)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (
|
|
51
|
+
this.pipelineStep === "balancing_l_shapes" &&
|
|
52
|
+
this.traceIdQueue.length === 0
|
|
53
|
+
) {
|
|
54
|
+
this.solved = true
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const targetMspConnectionPairId = this.traceIdQueue.shift()!
|
|
59
|
+
this.activeTraceId = targetMspConnectionPairId // Set active trace ID
|
|
60
|
+
const originalTrace = this.tracesMap.get(targetMspConnectionPairId)!
|
|
61
|
+
|
|
62
|
+
const { tracePath } = originalTrace
|
|
63
|
+
|
|
64
|
+
// Skip if the trace is a perfect rectangle
|
|
65
|
+
const is4PointRectangle = (path: typeof tracePath): boolean => {
|
|
66
|
+
if (path.length !== 4) return false
|
|
67
|
+
const [p0, p1, p2, p3] = path
|
|
68
|
+
// H-V-H "C" shape
|
|
69
|
+
const isHVHC =
|
|
70
|
+
p0.y === p1.y && p1.x === p2.x && p2.y === p3.y && p0.x === p3.x
|
|
71
|
+
// V-H-V "C" shape
|
|
72
|
+
const isVHVC =
|
|
73
|
+
p0.x === p1.x && p1.y === p2.y && p2.x === p3.x && p0.y === p3.y
|
|
74
|
+
return isHVHC || isVHVC
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (is4PointRectangle(tracePath)) {
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const allTraces = Array.from(this.tracesMap.values())
|
|
82
|
+
|
|
83
|
+
let updatedTrace: SolvedTracePath
|
|
84
|
+
|
|
85
|
+
if (this.pipelineStep === "minimizing_turns") {
|
|
86
|
+
updatedTrace = minimizeTurnsWithFilteredLabels({
|
|
87
|
+
...this.input,
|
|
88
|
+
targetMspConnectionPairId,
|
|
89
|
+
traces: allTraces,
|
|
90
|
+
})
|
|
91
|
+
} else {
|
|
92
|
+
updatedTrace = balanceZShapes({
|
|
93
|
+
...this.input,
|
|
94
|
+
targetMspConnectionPairId,
|
|
95
|
+
traces: allTraces,
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Update the state in the central map
|
|
100
|
+
this.tracesMap.set(targetMspConnectionPairId, updatedTrace)
|
|
101
|
+
|
|
102
|
+
// Update the output for visualization
|
|
103
|
+
this.outputTraces = Array.from(this.tracesMap.values())
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getOutput() {
|
|
107
|
+
return {
|
|
108
|
+
traces: this.outputTraces,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override visualize(): GraphicsObject {
|
|
113
|
+
const graphics = visualizeInputProblem(this.input.inputProblem, {
|
|
114
|
+
chipAlpha: 0.1,
|
|
115
|
+
connectionAlpha: 0.1,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
if (!graphics.lines) graphics.lines = []
|
|
119
|
+
if (!graphics.points) graphics.points = []
|
|
120
|
+
if (!graphics.rects) graphics.rects = []
|
|
121
|
+
if (!graphics.circles) graphics.circles = []
|
|
122
|
+
if (!graphics.texts) graphics.texts = []
|
|
123
|
+
|
|
124
|
+
for (const trace of this.outputTraces) {
|
|
125
|
+
const line: Line = {
|
|
126
|
+
points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })),
|
|
127
|
+
strokeColor: trace.mspPairId === this.activeTraceId ? "red" : "blue", // Highlight active trace
|
|
128
|
+
}
|
|
129
|
+
graphics.lines!.push(line)
|
|
130
|
+
}
|
|
131
|
+
return graphics
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type { Point } from "graphics-debug"
|
|
2
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
3
|
+
import { simplifyPath } from "./simplifyPath"
|
|
4
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
5
|
+
import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
6
|
+
import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
7
|
+
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
8
|
+
|
|
9
|
+
export const balanceZShapes = ({
|
|
10
|
+
targetMspConnectionPairId,
|
|
11
|
+
traces,
|
|
12
|
+
inputProblem,
|
|
13
|
+
allLabelPlacements,
|
|
14
|
+
mergedLabelNetIdMap,
|
|
15
|
+
paddingBuffer,
|
|
16
|
+
}: {
|
|
17
|
+
targetMspConnectionPairId: string
|
|
18
|
+
traces: SolvedTracePath[]
|
|
19
|
+
inputProblem: InputProblem
|
|
20
|
+
allLabelPlacements: NetLabelPlacement[]
|
|
21
|
+
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
22
|
+
paddingBuffer: number
|
|
23
|
+
}): SolvedTracePath => {
|
|
24
|
+
const targetTrace = traces.find(
|
|
25
|
+
(t) => t.mspPairId === targetMspConnectionPairId,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
if (!targetTrace) {
|
|
29
|
+
throw new Error(`Target trace ${targetMspConnectionPairId} not found`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const TOLERANCE = 1e-5
|
|
33
|
+
|
|
34
|
+
const obstacleTraces = traces.filter(
|
|
35
|
+
(t) => t.mspPairId !== targetMspConnectionPairId,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const TRACE_WIDTH = 0.01
|
|
39
|
+
const traceObstacles = obstacleTraces.flatMap((trace, i) =>
|
|
40
|
+
trace.tracePath.slice(0, -1).map((p1, pi) => {
|
|
41
|
+
const p2 = trace.tracePath[pi + 1]!
|
|
42
|
+
return {
|
|
43
|
+
chipId: `trace-obstacle-${i}-${pi}`,
|
|
44
|
+
minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2,
|
|
45
|
+
minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2,
|
|
46
|
+
maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2,
|
|
47
|
+
maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2,
|
|
48
|
+
}
|
|
49
|
+
}),
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
const staticObstacles = getObstacleRects(inputProblem).map((obs) => ({
|
|
53
|
+
...obs,
|
|
54
|
+
minX: obs.minX + TOLERANCE,
|
|
55
|
+
maxX: obs.maxX - TOLERANCE,
|
|
56
|
+
minY: obs.minY + TOLERANCE,
|
|
57
|
+
maxY: obs.maxY - TOLERANCE,
|
|
58
|
+
}))
|
|
59
|
+
|
|
60
|
+
const combinedObstacles = [...staticObstacles, ...traceObstacles]
|
|
61
|
+
|
|
62
|
+
const segmentIntersectsAnyRect = (
|
|
63
|
+
p1: Point,
|
|
64
|
+
p2: Point,
|
|
65
|
+
rects: any[],
|
|
66
|
+
): boolean => {
|
|
67
|
+
for (const rect of rects) {
|
|
68
|
+
if (segmentIntersectsRect(p1, p2, rect)) {
|
|
69
|
+
return true
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
76
|
+
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId]
|
|
77
|
+
if (originalNetIds) {
|
|
78
|
+
return !originalNetIds.has(targetTrace.globalConnNetId)
|
|
79
|
+
}
|
|
80
|
+
return label.globalConnNetId !== targetTrace.globalConnNetId
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
const labelBounds = filteredLabels.map((nl) => ({
|
|
84
|
+
minX: nl.center.x - nl.width / 2 + TOLERANCE,
|
|
85
|
+
maxX: nl.center.x + nl.width / 2 - TOLERANCE,
|
|
86
|
+
minY: nl.center.y - nl.height / 2 + TOLERANCE,
|
|
87
|
+
maxY: nl.center.y + nl.height / 2 - TOLERANCE,
|
|
88
|
+
}))
|
|
89
|
+
|
|
90
|
+
const newPath = [...targetTrace.tracePath]
|
|
91
|
+
|
|
92
|
+
if (newPath.length < 4) {
|
|
93
|
+
return { ...targetTrace }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (newPath.length === 4) {
|
|
97
|
+
const [p0, p1, p2, p3] = newPath
|
|
98
|
+
let p1New: Point
|
|
99
|
+
let p2New: Point
|
|
100
|
+
|
|
101
|
+
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y
|
|
102
|
+
|
|
103
|
+
if (isHVHShape) {
|
|
104
|
+
const idealX = (p0.x + p3.x) / 2
|
|
105
|
+
p1New = { x: idealX, y: p1.y }
|
|
106
|
+
p2New = { x: idealX, y: p2.y }
|
|
107
|
+
} else {
|
|
108
|
+
const idealY = (p0.y + p3.y) / 2
|
|
109
|
+
p1New = { x: p1.x, y: idealY }
|
|
110
|
+
p2New = { x: p2.x, y: idealY }
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const collides =
|
|
114
|
+
segmentIntersectsAnyRect(p0, p1New, combinedObstacles) ||
|
|
115
|
+
segmentIntersectsAnyRect(p1New, p2New, combinedObstacles) ||
|
|
116
|
+
segmentIntersectsAnyRect(p2New, p3, combinedObstacles) ||
|
|
117
|
+
segmentIntersectsAnyRect(p0, p1New, labelBounds) ||
|
|
118
|
+
segmentIntersectsAnyRect(p1New, p2New, labelBounds) ||
|
|
119
|
+
segmentIntersectsAnyRect(p2New, p3, labelBounds)
|
|
120
|
+
|
|
121
|
+
if (!collides) {
|
|
122
|
+
newPath[1] = p1New
|
|
123
|
+
newPath[2] = p2New
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { ...targetTrace, tracePath: simplifyPath(newPath) }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (let i = 1; i < newPath.length - 4; i++) {
|
|
130
|
+
const p1 = newPath[i]!
|
|
131
|
+
const p2 = newPath[i + 1]!
|
|
132
|
+
const p3 = newPath[i + 2]!
|
|
133
|
+
const p4 = newPath[i + 3]!
|
|
134
|
+
|
|
135
|
+
const isHVHZShape = p1.y === p2.y && p2.x === p3.x && p3.y === p4.y
|
|
136
|
+
const isVHVZShape = p1.x === p2.x && p2.y === p3.y && p3.x === p4.x
|
|
137
|
+
|
|
138
|
+
const isCollinearHorizontal =
|
|
139
|
+
p1.y === p2.y && p2.y === p3.y && p3.y === p4.y
|
|
140
|
+
const isCollinearVertical = p1.x === p2.x && p2.x === p3.x && p3.x === p4.x
|
|
141
|
+
const isCollinear = isCollinearHorizontal || isCollinearVertical
|
|
142
|
+
|
|
143
|
+
let isSameDirection = false
|
|
144
|
+
if (isHVHZShape) {
|
|
145
|
+
isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x)
|
|
146
|
+
} else if (isVHVZShape) {
|
|
147
|
+
isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const isValidZShape =
|
|
151
|
+
(isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection
|
|
152
|
+
|
|
153
|
+
if (!isValidZShape) {
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let p2New: Point
|
|
158
|
+
let p3New: Point
|
|
159
|
+
const len1Original = isHVHZShape
|
|
160
|
+
? Math.abs(p1.x - p2.x)
|
|
161
|
+
: Math.abs(p1.y - p2.y)
|
|
162
|
+
const len2Original = isHVHZShape
|
|
163
|
+
? Math.abs(p3.x - p4.x)
|
|
164
|
+
: Math.abs(p3.y - p4.y)
|
|
165
|
+
|
|
166
|
+
if (Math.abs(len1Original - len2Original) < 0.001) {
|
|
167
|
+
continue
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (isHVHZShape) {
|
|
171
|
+
const idealX = (p1.x + p4.x) / 2
|
|
172
|
+
p2New = { x: idealX, y: p2.y }
|
|
173
|
+
p3New = { x: idealX, y: p3.y }
|
|
174
|
+
} else {
|
|
175
|
+
const idealY = (p1.y + p4.y) / 2
|
|
176
|
+
p2New = { x: p2.x, y: idealY }
|
|
177
|
+
p3New = { x: p3.x, y: idealY }
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const collides =
|
|
181
|
+
segmentIntersectsAnyRect(p1, p2New, combinedObstacles) ||
|
|
182
|
+
segmentIntersectsAnyRect(p2New, p3New, combinedObstacles) ||
|
|
183
|
+
segmentIntersectsAnyRect(p3New, p4, combinedObstacles) ||
|
|
184
|
+
segmentIntersectsAnyRect(p1, p2New, labelBounds) ||
|
|
185
|
+
segmentIntersectsAnyRect(p2New, p3New, labelBounds) ||
|
|
186
|
+
segmentIntersectsAnyRect(p3New, p4, labelBounds)
|
|
187
|
+
|
|
188
|
+
if (!collides) {
|
|
189
|
+
newPath[i + 1] = p2New
|
|
190
|
+
newPath[i + 2] = p3New
|
|
191
|
+
i = 0
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const finalSimplifiedPath = simplifyPath(newPath)
|
|
196
|
+
return {
|
|
197
|
+
...targetTrace,
|
|
198
|
+
tracePath: finalSimplifiedPath,
|
|
199
|
+
}
|
|
200
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
2
|
+
import { minimizeTurns } from "./turnMinimization"
|
|
3
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
|
+
import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
5
|
+
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
6
|
+
|
|
7
|
+
export const minimizeTurnsWithFilteredLabels = ({
|
|
8
|
+
targetMspConnectionPairId,
|
|
9
|
+
traces,
|
|
10
|
+
inputProblem,
|
|
11
|
+
allLabelPlacements,
|
|
12
|
+
mergedLabelNetIdMap,
|
|
13
|
+
paddingBuffer,
|
|
14
|
+
}: {
|
|
15
|
+
targetMspConnectionPairId: string
|
|
16
|
+
traces: SolvedTracePath[]
|
|
17
|
+
inputProblem: InputProblem
|
|
18
|
+
allLabelPlacements: NetLabelPlacement[]
|
|
19
|
+
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
20
|
+
paddingBuffer: number
|
|
21
|
+
}): SolvedTracePath => {
|
|
22
|
+
const targetTrace = traces.find(
|
|
23
|
+
(t) => t.mspPairId === targetMspConnectionPairId,
|
|
24
|
+
)
|
|
25
|
+
if (!targetTrace) {
|
|
26
|
+
throw new Error(`Target trace ${targetMspConnectionPairId} not found`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const obstacleTraces = traces.filter(
|
|
30
|
+
(t) => t.mspPairId !== targetMspConnectionPairId,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const TRACE_WIDTH = 0.01
|
|
34
|
+
const traceObstacles = obstacleTraces.flatMap((trace, i) =>
|
|
35
|
+
trace.tracePath.slice(0, -1).map((p1, pi) => {
|
|
36
|
+
const p2 = trace.tracePath[pi + 1]!
|
|
37
|
+
return {
|
|
38
|
+
chipId: `trace-obstacle-${i}-${pi}`,
|
|
39
|
+
minX: Math.min(p1.x, p2.x) - TRACE_WIDTH / 2,
|
|
40
|
+
minY: Math.min(p1.y, p2.y) - TRACE_WIDTH / 2,
|
|
41
|
+
maxX: Math.max(p1.x, p2.x) + TRACE_WIDTH / 2,
|
|
42
|
+
maxY: Math.max(p1.y, p2.y) + TRACE_WIDTH / 2,
|
|
43
|
+
}
|
|
44
|
+
}),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const staticObstacles = getObstacleRects(inputProblem)
|
|
48
|
+
const combinedObstacles = [...staticObstacles, ...traceObstacles]
|
|
49
|
+
|
|
50
|
+
const originalPath = targetTrace.tracePath
|
|
51
|
+
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
52
|
+
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId]
|
|
53
|
+
if (originalNetIds) {
|
|
54
|
+
return !originalNetIds.has(targetTrace.globalConnNetId)
|
|
55
|
+
}
|
|
56
|
+
return label.globalConnNetId !== targetTrace.globalConnNetId
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const labelBounds = filteredLabels.map((nl) => ({
|
|
60
|
+
minX: nl.center.x - nl.width / 2 - paddingBuffer,
|
|
61
|
+
maxX: nl.center.x + nl.width / 2 + paddingBuffer,
|
|
62
|
+
minY: nl.center.y - nl.height / 2 - paddingBuffer,
|
|
63
|
+
maxY: nl.center.y + nl.height / 2 + paddingBuffer,
|
|
64
|
+
}))
|
|
65
|
+
|
|
66
|
+
const newPath = minimizeTurns({
|
|
67
|
+
path: originalPath,
|
|
68
|
+
obstacles: combinedObstacles,
|
|
69
|
+
labelBounds,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
...targetTrace,
|
|
74
|
+
tracePath: newPath,
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -5,7 +5,6 @@ import type { GraphicsObject } from "graphics-debug"
|
|
|
5
5
|
import { visualizeInputProblem } from "../SchematicTracePipelineSolver/visualizeInputProblem"
|
|
6
6
|
import type { InputProblem } from "../../types/InputProblem"
|
|
7
7
|
import { MergedNetLabelObstacleSolver } from "./sub-solvers/LabelMergingSolver/LabelMergingSolver"
|
|
8
|
-
import { TraceCleanupSolver } from "./sub-solvers/TraceCleanupSolver/TraceCleanupSolver"
|
|
9
8
|
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
10
9
|
import { OverlapAvoidanceStepSolver } from "./sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver"
|
|
11
10
|
|
|
@@ -27,7 +26,6 @@ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver {
|
|
|
27
26
|
// sub-solver instances
|
|
28
27
|
labelMergingSolver?: MergedNetLabelObstacleSolver
|
|
29
28
|
overlapAvoidanceSolver?: OverlapAvoidanceStepSolver
|
|
30
|
-
traceCleanupSolver?: TraceCleanupSolver
|
|
31
29
|
pipelineStepIndex = 0
|
|
32
30
|
|
|
33
31
|
constructor(solverInput: TraceLabelOverlapAvoidanceSolverInput) {
|
|
@@ -75,24 +73,6 @@ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver {
|
|
|
75
73
|
this.activeSubSolver = this.overlapAvoidanceSolver
|
|
76
74
|
break
|
|
77
75
|
|
|
78
|
-
case 2:
|
|
79
|
-
this.traceCleanupSolver = new TraceCleanupSolver({
|
|
80
|
-
inputProblem: this.inputProblem,
|
|
81
|
-
allTraces: this.overlapAvoidanceSolver!.getOutput().allTraces,
|
|
82
|
-
targetTraceIds: new Set(
|
|
83
|
-
this.overlapAvoidanceSolver!.getOutput().modifiedTraces.map(
|
|
84
|
-
(t) => t.mspPairId,
|
|
85
|
-
),
|
|
86
|
-
),
|
|
87
|
-
allLabelPlacements:
|
|
88
|
-
this.labelMergingSolver!.getOutput().netLabelPlacements,
|
|
89
|
-
mergedLabelNetIdMap:
|
|
90
|
-
this.labelMergingSolver!.getOutput().mergedLabelNetIdMap,
|
|
91
|
-
paddingBuffer: 0.01,
|
|
92
|
-
})
|
|
93
|
-
this.activeSubSolver = this.traceCleanupSolver
|
|
94
|
-
break
|
|
95
|
-
|
|
96
76
|
default:
|
|
97
77
|
this.solved = true
|
|
98
78
|
break
|
|
@@ -101,7 +81,7 @@ export class TraceLabelOverlapAvoidanceSolver extends BaseSolver {
|
|
|
101
81
|
|
|
102
82
|
getOutput() {
|
|
103
83
|
return {
|
|
104
|
-
traces: this.
|
|
84
|
+
traces: this.overlapAvoidanceSolver?.getOutput().allTraces ?? this.traces,
|
|
105
85
|
netLabelPlacements:
|
|
106
86
|
this.labelMergingSolver?.getOutput().netLabelPlacements ??
|
|
107
87
|
this.netLabelPlacements,
|
|
@@ -5,8 +5,8 @@ import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacemen
|
|
|
5
5
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
6
6
|
import { findTraceViolationZone } from "./violation"
|
|
7
7
|
import { generateSnipAndReconnectCandidates } from "./trySnipAndReconnect"
|
|
8
|
-
import { simplifyPath } from "./sub-solvers/TraceCleanupSolver/simplifyPath"
|
|
9
8
|
import { generateFourPointDetourCandidates } from "./tryFourPointDetour"
|
|
9
|
+
import { simplifyPath } from "../TraceCleanupSolver/simplifyPath"
|
|
10
10
|
|
|
11
11
|
export const generateRerouteCandidates = ({
|
|
12
12
|
trace,
|
|
@@ -8,7 +8,7 @@ import { isPathCollidingWithObstacles } from "lib/solvers/SchematicTraceLinesSol
|
|
|
8
8
|
import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
9
9
|
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
10
10
|
import { generateRerouteCandidates } from "../../rerouteCollidingTrace"
|
|
11
|
-
import { simplifyPath } from "
|
|
11
|
+
import { simplifyPath } from "lib/solvers/TraceCleanupSolver/simplifyPath"
|
|
12
12
|
|
|
13
13
|
interface SingleOverlapSolverInput {
|
|
14
14
|
trace: SolvedTracePath
|
package/package.json
CHANGED