@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
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { test, expect } from "bun:test"
|
|
2
|
+
import type { InputProblem } from "lib/index"
|
|
3
|
+
import { SchematicTracePipelineSolver } from "lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver"
|
|
4
|
+
import "tests/fixtures/matcher"
|
|
5
|
+
import inputProblem from "../assets/example26.json"
|
|
6
|
+
|
|
7
|
+
test("example29", () => {
|
|
8
|
+
const solver = new SchematicTracePipelineSolver(inputProblem as any)
|
|
9
|
+
|
|
10
|
+
solver.solve()
|
|
11
|
+
|
|
12
|
+
expect(solver).toMatchSolverSnapshot(import.meta.path)
|
|
13
|
+
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect } from "bun:test"
|
|
2
2
|
import { test } from "bun:test"
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import inputData from "../../assets/TraceCleanupSolver.test.input.json"
|
|
4
|
+
import { TraceCleanupSolver } from "lib/solvers/TraceCleanupSolver/TraceCleanupSolver"
|
|
5
5
|
|
|
6
6
|
test("TraceCleanupSolver snapshot", () => {
|
|
7
7
|
const solver = new TraceCleanupSolver({
|
|
@@ -51,7 +51,7 @@ x-" data-x="1.6" data-y="-2.295" cx="353.6" cy="465.31999999999994" r="3" fill="
|
|
|
51
51
|
<polyline data-points="1.2000000000000002,0.30000000000000004 1.6,-1.895" data-type="line" data-label="" points="308.80000000000007,174.68 353.6,420.52" fill="none" stroke="hsl(190, 100%, 50%, 0.1)" stroke-width="1" stroke-dasharray="4 2" />
|
|
52
52
|
</g>
|
|
53
53
|
<g>
|
|
54
|
-
<polyline data-points="1.2000000000000002,-0.30000000000000004 1.4000000000000001,-0.30000000000000004 1.4000000000000001,-1.2975000000000003 1.049,-1.2975000000000003 1.049,-2.2950000000000004 1.6,-2.295" data-type="line" data-label="" points="308.80000000000007,241.88 331.20000000000005,241.88 331.20000000000005,353.6 291.88800000000003,353.6 291.88800000000003,465.32000000000005 353.6,465.31999999999994" fill="none" stroke="
|
|
54
|
+
<polyline data-points="1.2000000000000002,-0.30000000000000004 1.4000000000000001,-0.30000000000000004 1.4000000000000001,-1.2975000000000003 1.049,-1.2975000000000003 1.049,-2.2950000000000004 1.6,-2.295" data-type="line" data-label="" points="308.80000000000007,241.88 331.20000000000005,241.88 331.20000000000005,353.6 291.88800000000003,353.6 291.88800000000003,465.32000000000005 353.6,465.31999999999994" fill="none" stroke="red" stroke-width="1" />
|
|
55
55
|
</g>
|
|
56
56
|
<g>
|
|
57
57
|
<rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="40" y="152.28" width="268.80000000000007" height="111.99999999999997" fill="hsl(24, 100%, 50%, 0.1)" stroke="black" stroke-width="0.008928571428571428" />
|
|
@@ -107,7 +107,7 @@ x-" data-x="7.850000000000001" data-y="-2.295" cx="490.488888888889" cy="391.586
|
|
|
107
107
|
<polyline data-points="7.450000000000001,0.30000000000000004 7.850000000000001,-1.895" data-type="line" data-label="" points="470.57777777777784,262.41333333333336 490.488888888889,371.6755555555556" fill="none" stroke="hsl(190, 100%, 50%, 0.1)" stroke-width="1" stroke-dasharray="4 2" />
|
|
108
108
|
</g>
|
|
109
109
|
<g>
|
|
110
|
-
<polyline data-points="7.450000000000001,-0.30000000000000004 7.650000000000001,-0.30000000000000004 7.650000000000001,-1.2975000000000003 7.299000000000001,-1.2975000000000003 7.299000000000001,-2.2950000000000004 7.850000000000001,-2.295" data-type="line" data-label="" points="470.57777777777784,292.28000000000003 480.5333333333334,292.28000000000003 480.5333333333334,341.9333333333334 463.06133333333344,341.9333333333334 463.06133333333344,391.5866666666667 490.488888888889,391.5866666666667" fill="none" stroke="
|
|
110
|
+
<polyline data-points="7.450000000000001,-0.30000000000000004 7.650000000000001,-0.30000000000000004 7.650000000000001,-1.2975000000000003 7.299000000000001,-1.2975000000000003 7.299000000000001,-2.2950000000000004 7.850000000000001,-2.295" data-type="line" data-label="" points="470.57777777777784,292.28000000000003 480.5333333333334,292.28000000000003 480.5333333333334,341.9333333333334 463.06133333333344,341.9333333333334 463.06133333333344,391.5866666666667 490.488888888889,391.5866666666667" fill="none" stroke="red" stroke-width="1" />
|
|
111
111
|
</g>
|
|
112
112
|
<g>
|
|
113
113
|
<rect data-type="rect" data-label="schematic_component_0" data-x="0" data-y="0" x="40.00000000000001" y="252.4577777777778" width="119.4666666666667" height="49.7777777777778" fill="hsl(24, 100%, 50%, 0.1)" stroke="black" stroke-width="0.020089285714285712" />
|
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
stackGraphicsHorizontally,
|
|
6
6
|
} from "graphics-debug"
|
|
7
7
|
import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver"
|
|
8
|
-
import { TraceCleanupSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver"
|
|
9
8
|
import inputData from "../../../assets/3.input.json"
|
|
9
|
+
import { TraceCleanupSolver } from "lib/solvers/TraceCleanupSolver/TraceCleanupSolver"
|
|
10
10
|
|
|
11
11
|
test("SingleOverlapSolver-to-TraceCleanupSolver snapshot", () => {
|
|
12
12
|
// Convert targetTraceIds to Set
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import type { InputProblem } from "lib/types/InputProblem"
|
|
2
|
-
import type { GraphicsObject, Line } from "graphics-debug"
|
|
3
|
-
import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
4
|
-
import { minimizeTurnsWithFilteredLabels } from "./minimizeTurnsWithFilteredLabels"
|
|
5
|
-
import { balanceLShapes } from "./balanceLShapes"
|
|
6
|
-
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
7
|
-
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
8
|
-
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
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
|
-
/**
|
|
20
|
-
* Cleans up traces by minimizing turns and balancing L-shapes to improve
|
|
21
|
-
* the overall aesthetics and readability of the schematic.
|
|
22
|
-
*/
|
|
23
|
-
export class TraceCleanupSolver extends BaseSolver {
|
|
24
|
-
private input: TraceCleanupSolverInput
|
|
25
|
-
private outputTraces: SolvedTracePath[]
|
|
26
|
-
private pipelineStepIndex = 0
|
|
27
|
-
private tracesToProcess: SolvedTracePath[]
|
|
28
|
-
private minimizedTraces: SolvedTracePath[] | null = null
|
|
29
|
-
private balancedTraces: SolvedTracePath[] | null = null
|
|
30
|
-
|
|
31
|
-
constructor(solverInput: TraceCleanupSolverInput) {
|
|
32
|
-
super()
|
|
33
|
-
this.input = solverInput
|
|
34
|
-
this.outputTraces = [...solverInput.allTraces]
|
|
35
|
-
this.tracesToProcess = this.outputTraces.filter((t) =>
|
|
36
|
-
this.input.targetTraceIds.has(t.mspPairId),
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
override _step() {
|
|
41
|
-
const {
|
|
42
|
-
targetTraceIds,
|
|
43
|
-
inputProblem,
|
|
44
|
-
allLabelPlacements,
|
|
45
|
-
mergedLabelNetIdMap,
|
|
46
|
-
paddingBuffer,
|
|
47
|
-
} = this.input
|
|
48
|
-
|
|
49
|
-
if (targetTraceIds.size === 0) {
|
|
50
|
-
this.solved = true
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
switch (this.pipelineStepIndex) {
|
|
55
|
-
case 0: {
|
|
56
|
-
// Step 0: Minimize turns
|
|
57
|
-
const minimizedTracesResult = minimizeTurnsWithFilteredLabels({
|
|
58
|
-
traces: this.tracesToProcess,
|
|
59
|
-
inputProblem,
|
|
60
|
-
allLabelPlacements,
|
|
61
|
-
mergedLabelNetIdMap,
|
|
62
|
-
paddingBuffer,
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
this.minimizedTraces = minimizedTracesResult ?? this.tracesToProcess
|
|
66
|
-
|
|
67
|
-
const tracesMap = new Map(
|
|
68
|
-
this.input.allTraces.map((t) => [t.mspPairId, t]),
|
|
69
|
-
)
|
|
70
|
-
for (const trace of this.minimizedTraces) {
|
|
71
|
-
tracesMap.set(trace.mspPairId, trace)
|
|
72
|
-
}
|
|
73
|
-
this.outputTraces = Array.from(tracesMap.values())
|
|
74
|
-
this.pipelineStepIndex++
|
|
75
|
-
break
|
|
76
|
-
}
|
|
77
|
-
case 1: {
|
|
78
|
-
// Step 1: Balance L-shapes
|
|
79
|
-
const balancedTracesResult = balanceLShapes({
|
|
80
|
-
traces: this.minimizedTraces!,
|
|
81
|
-
inputProblem,
|
|
82
|
-
allLabelPlacements,
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
this.balancedTraces = balancedTracesResult ?? this.minimizedTraces
|
|
86
|
-
|
|
87
|
-
const tracesMap = new Map(
|
|
88
|
-
this.input.allTraces.map((t) => [t.mspPairId, t]),
|
|
89
|
-
)
|
|
90
|
-
for (const trace of this.balancedTraces!) {
|
|
91
|
-
tracesMap.set(trace.mspPairId, trace)
|
|
92
|
-
}
|
|
93
|
-
this.outputTraces = Array.from(tracesMap.values())
|
|
94
|
-
this.pipelineStepIndex++
|
|
95
|
-
break
|
|
96
|
-
}
|
|
97
|
-
case 2: {
|
|
98
|
-
// Step 2: Done
|
|
99
|
-
this.solved = true
|
|
100
|
-
break
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
getOutput() {
|
|
106
|
-
return {
|
|
107
|
-
traces: this.outputTraces,
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
override visualize(): GraphicsObject {
|
|
112
|
-
const graphics = visualizeInputProblem(this.input.inputProblem, {
|
|
113
|
-
chipAlpha: 0.1,
|
|
114
|
-
connectionAlpha: 0.1,
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
if (!graphics.lines) graphics.lines = []
|
|
118
|
-
if (!graphics.points) graphics.points = []
|
|
119
|
-
if (!graphics.rects) graphics.rects = []
|
|
120
|
-
if (!graphics.circles) graphics.circles = []
|
|
121
|
-
if (!graphics.texts) graphics.texts = []
|
|
122
|
-
|
|
123
|
-
for (const trace of this.outputTraces) {
|
|
124
|
-
const line: Line = {
|
|
125
|
-
points: trace.tracePath.map((p) => ({ x: p.x, y: p.y })),
|
|
126
|
-
strokeColor: "blue",
|
|
127
|
-
}
|
|
128
|
-
graphics.lines!.push(line)
|
|
129
|
-
}
|
|
130
|
-
return graphics
|
|
131
|
-
}
|
|
132
|
-
}
|
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
import type { Point } from "graphics-debug"
|
|
2
|
-
import type { InputProblem } from "lib/types/InputProblem"
|
|
3
|
-
import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
4
|
-
import { simplifyPath } from "./simplifyPath"
|
|
5
|
-
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
6
|
-
import { segmentIntersectsRect } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
7
|
-
import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
8
|
-
|
|
9
|
-
export const balanceLShapes = ({
|
|
10
|
-
traces,
|
|
11
|
-
inputProblem,
|
|
12
|
-
allLabelPlacements,
|
|
13
|
-
}: {
|
|
14
|
-
traces: SolvedTracePath[]
|
|
15
|
-
inputProblem: InputProblem
|
|
16
|
-
allLabelPlacements: NetLabelPlacement[]
|
|
17
|
-
}): SolvedTracePath[] | null => {
|
|
18
|
-
const TOLERANCE = 1e-5
|
|
19
|
-
let changesMade = false
|
|
20
|
-
|
|
21
|
-
for (const trace of traces) {
|
|
22
|
-
if (trace.tracePath.length === 4) {
|
|
23
|
-
const [p0, p1, p2, p3] = trace.tracePath
|
|
24
|
-
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y
|
|
25
|
-
const isVHVShape = p0.x === p1.x && p1.y === p2.y && p2.x === p3.x
|
|
26
|
-
|
|
27
|
-
const isCollinearHorizontal =
|
|
28
|
-
p0.y === p1.y && p1.y === p2.y && p2.y === p3.y
|
|
29
|
-
const isCollinearVertical =
|
|
30
|
-
p0.x === p1.x && p1.x === p2.x && p2.x === p3.x
|
|
31
|
-
const isCollinear = isCollinearHorizontal || isCollinearVertical
|
|
32
|
-
|
|
33
|
-
let isSameDirection = false
|
|
34
|
-
if (isHVHShape) {
|
|
35
|
-
isSameDirection = Math.sign(p1.x - p0.x) === Math.sign(p3.x - p2.x)
|
|
36
|
-
} else if (isVHVShape) {
|
|
37
|
-
isSameDirection = Math.sign(p1.y - p0.y) === Math.sign(p3.y - p2.y)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const isValidZShape =
|
|
41
|
-
(isHVHShape || isVHVShape) && !isCollinear && isSameDirection
|
|
42
|
-
|
|
43
|
-
if (!isValidZShape) {
|
|
44
|
-
return null
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const obstacles = getObstacleRects(inputProblem).map((obs) => ({
|
|
50
|
-
...obs,
|
|
51
|
-
minX: obs.minX + TOLERANCE,
|
|
52
|
-
maxX: obs.maxX - TOLERANCE,
|
|
53
|
-
minY: obs.minY + TOLERANCE,
|
|
54
|
-
maxY: obs.maxY - TOLERANCE,
|
|
55
|
-
}))
|
|
56
|
-
|
|
57
|
-
const segmentIntersectsAnyRect = (
|
|
58
|
-
p1: Point,
|
|
59
|
-
p2: Point,
|
|
60
|
-
rects: any[],
|
|
61
|
-
): boolean => {
|
|
62
|
-
for (const rect of rects) {
|
|
63
|
-
if (segmentIntersectsRect(p1, p2, rect)) {
|
|
64
|
-
return true
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return false
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const getLabelBounds = (labels: NetLabelPlacement[], traceNetId: string) => {
|
|
71
|
-
const filteredLabels = labels.filter(
|
|
72
|
-
(label) => label.globalConnNetId !== traceNetId,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
return filteredLabels.map((nl) => ({
|
|
76
|
-
minX: nl.center.x - nl.width / 2 + TOLERANCE,
|
|
77
|
-
maxX: nl.center.x + nl.width / 2 - TOLERANCE,
|
|
78
|
-
minY: nl.center.y - nl.height / 2 + TOLERANCE,
|
|
79
|
-
maxY: nl.center.y + nl.height / 2 - TOLERANCE,
|
|
80
|
-
}))
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const newTraces = traces.map((trace) => {
|
|
84
|
-
const newPath = [...trace.tracePath]
|
|
85
|
-
|
|
86
|
-
if (newPath.length < 4) {
|
|
87
|
-
return { ...trace }
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const labelBounds = getLabelBounds(
|
|
91
|
-
allLabelPlacements,
|
|
92
|
-
trace.globalConnNetId,
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
if (newPath.length === 4) {
|
|
96
|
-
const [p0, p1, p2, p3] = newPath
|
|
97
|
-
let p1New: Point
|
|
98
|
-
let p2New: Point
|
|
99
|
-
|
|
100
|
-
const isHVHShape = p0.y === p1.y && p1.x === p2.x && p2.y === p3.y
|
|
101
|
-
|
|
102
|
-
if (isHVHShape) {
|
|
103
|
-
const idealX = (p0.x + p3.x) / 2
|
|
104
|
-
p1New = { x: idealX, y: p1.y }
|
|
105
|
-
p2New = { x: idealX, y: p2.y }
|
|
106
|
-
} else {
|
|
107
|
-
const idealY = (p0.y + p3.y) / 2
|
|
108
|
-
p1New = { x: p1.x, y: idealY }
|
|
109
|
-
p2New = { x: p2.x, y: idealY }
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const collides =
|
|
113
|
-
segmentIntersectsAnyRect(p0, p1New, obstacles) ||
|
|
114
|
-
segmentIntersectsAnyRect(p1New, p2New, obstacles) ||
|
|
115
|
-
segmentIntersectsAnyRect(p2New, p3, obstacles) ||
|
|
116
|
-
segmentIntersectsAnyRect(p0, p1New, labelBounds) ||
|
|
117
|
-
segmentIntersectsAnyRect(p1New, p2New, labelBounds) ||
|
|
118
|
-
segmentIntersectsAnyRect(p2New, p3, labelBounds)
|
|
119
|
-
|
|
120
|
-
if (!collides) {
|
|
121
|
-
newPath[1] = p1New
|
|
122
|
-
newPath[2] = p2New
|
|
123
|
-
changesMade = true
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return { ...trace, 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 =
|
|
141
|
-
p1.x === p2.x && p2.x === p3.x && p3.x === p4.x
|
|
142
|
-
const isCollinear = isCollinearHorizontal || isCollinearVertical
|
|
143
|
-
|
|
144
|
-
let isSameDirection = false
|
|
145
|
-
if (isHVHZShape) {
|
|
146
|
-
isSameDirection = Math.sign(p2.x - p1.x) === Math.sign(p4.x - p3.x)
|
|
147
|
-
} else if (isVHVZShape) {
|
|
148
|
-
isSameDirection = Math.sign(p2.y - p1.y) === Math.sign(p4.y - p3.y)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const isValidZShape =
|
|
152
|
-
(isHVHZShape || isVHVZShape) && !isCollinear && isSameDirection
|
|
153
|
-
|
|
154
|
-
if (!isValidZShape) {
|
|
155
|
-
continue
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
let p2New: Point
|
|
159
|
-
let p3New: Point
|
|
160
|
-
const len1Original = isHVHZShape
|
|
161
|
-
? Math.abs(p1.x - p2.x)
|
|
162
|
-
: Math.abs(p1.y - p2.y)
|
|
163
|
-
const len2Original = isHVHZShape
|
|
164
|
-
? Math.abs(p3.x - p4.x)
|
|
165
|
-
: Math.abs(p3.y - p4.y)
|
|
166
|
-
|
|
167
|
-
if (Math.abs(len1Original - len2Original) < 0.001) {
|
|
168
|
-
continue
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (isHVHZShape) {
|
|
172
|
-
const idealX = (p1.x + p4.x) / 2
|
|
173
|
-
p2New = { x: idealX, y: p2.y }
|
|
174
|
-
p3New = { x: idealX, y: p3.y }
|
|
175
|
-
} else {
|
|
176
|
-
const idealY = (p1.y + p4.y) / 2
|
|
177
|
-
p2New = { x: p2.x, y: idealY }
|
|
178
|
-
p3New = { x: p3.x, y: idealY }
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const collides =
|
|
182
|
-
segmentIntersectsAnyRect(p1, p2New, obstacles) ||
|
|
183
|
-
segmentIntersectsAnyRect(p2New, p3New, obstacles) ||
|
|
184
|
-
segmentIntersectsAnyRect(p3New, p4, obstacles) ||
|
|
185
|
-
segmentIntersectsAnyRect(p1, p2New, labelBounds) ||
|
|
186
|
-
segmentIntersectsAnyRect(p2New, p3New, labelBounds) ||
|
|
187
|
-
segmentIntersectsAnyRect(p3New, p4, labelBounds)
|
|
188
|
-
|
|
189
|
-
if (!collides) {
|
|
190
|
-
newPath[i + 1] = p2New
|
|
191
|
-
newPath[i + 2] = p3New
|
|
192
|
-
changesMade = true
|
|
193
|
-
i = 0
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const finalSimplifiedPath = simplifyPath(newPath)
|
|
198
|
-
return {
|
|
199
|
-
...trace,
|
|
200
|
-
tracePath: finalSimplifiedPath,
|
|
201
|
-
}
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
if (changesMade) {
|
|
205
|
-
return newTraces
|
|
206
|
-
} else {
|
|
207
|
-
return null
|
|
208
|
-
}
|
|
209
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import type { InputProblem } from "lib/types/InputProblem"
|
|
2
|
-
import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
3
|
-
import { minimizeTurns } from "./turnMinimization"
|
|
4
|
-
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
5
|
-
import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
6
|
-
|
|
7
|
-
export const minimizeTurnsWithFilteredLabels = ({
|
|
8
|
-
traces,
|
|
9
|
-
inputProblem,
|
|
10
|
-
allLabelPlacements,
|
|
11
|
-
mergedLabelNetIdMap,
|
|
12
|
-
paddingBuffer,
|
|
13
|
-
}: {
|
|
14
|
-
traces: SolvedTracePath[]
|
|
15
|
-
inputProblem: InputProblem
|
|
16
|
-
allLabelPlacements: NetLabelPlacement[]
|
|
17
|
-
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
18
|
-
paddingBuffer: number
|
|
19
|
-
}): SolvedTracePath[] | null => {
|
|
20
|
-
let changesMade = false
|
|
21
|
-
const obstacles = getObstacleRects(inputProblem)
|
|
22
|
-
|
|
23
|
-
const newTraces = traces.map((trace) => {
|
|
24
|
-
const originalPath = trace.tracePath
|
|
25
|
-
const filteredLabels = allLabelPlacements.filter((label) => {
|
|
26
|
-
const originalNetIds = mergedLabelNetIdMap[label.globalConnNetId]
|
|
27
|
-
if (originalNetIds) {
|
|
28
|
-
return !originalNetIds.has(trace.globalConnNetId)
|
|
29
|
-
}
|
|
30
|
-
return label.globalConnNetId !== trace.globalConnNetId
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const labelBounds = filteredLabels.map((nl) => ({
|
|
34
|
-
minX: nl.center.x - nl.width / 2 - paddingBuffer,
|
|
35
|
-
maxX: nl.center.x + nl.width / 2 + paddingBuffer,
|
|
36
|
-
minY: nl.center.y - nl.height / 2 - paddingBuffer,
|
|
37
|
-
maxY: nl.center.y + nl.height / 2 + paddingBuffer,
|
|
38
|
-
}))
|
|
39
|
-
|
|
40
|
-
const newPath = minimizeTurns({
|
|
41
|
-
path: originalPath,
|
|
42
|
-
obstacles,
|
|
43
|
-
labelBounds,
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
if (
|
|
47
|
-
newPath.length !== originalPath.length ||
|
|
48
|
-
newPath.some(
|
|
49
|
-
(p, i) => p.x !== originalPath[i].x || p.y !== originalPath[i].y,
|
|
50
|
-
)
|
|
51
|
-
) {
|
|
52
|
-
changesMade = true
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return {
|
|
56
|
-
...trace,
|
|
57
|
-
tracePath: newPath,
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
if (changesMade) {
|
|
62
|
-
return newTraces
|
|
63
|
-
} else {
|
|
64
|
-
return null
|
|
65
|
-
}
|
|
66
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|