@tscircuit/schematic-trace-solver 0.0.41 → 0.0.42
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 +118 -7
- package/dist/index.js +760 -432
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +30 -17
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +94 -192
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +13 -24
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts +205 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +144 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver.ts +117 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/TraceCleanupSolver/TraceCleanupSolver.ts +132 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{balanceLShapes.ts → sub-solvers/TraceCleanupSolver/balanceLShapes.ts} +12 -10
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{hasCollisions.ts → sub-solvers/TraceCleanupSolver/hasCollisions.ts} +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{hasCollisionsWithLabels.ts → sub-solvers/TraceCleanupSolver/hasCollisionsWithLabels.ts} +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{minimizeTurnsWithFilteredLabels.ts → sub-solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts} +6 -6
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{simplifyPath.ts → sub-solvers/TraceCleanupSolver/simplifyPath.ts} +1 -1
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{turnMinimization.ts → sub-solvers/TraceCleanupSolver/turnMinimization.ts} +0 -2
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/tryFourPointDetour.ts +104 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +24 -154
- package/package.json +1 -1
- package/tests/assets/1.input.json +379 -0
- package/tests/assets/2.input.json +330 -0
- package/tests/assets/3.input.json +388 -0
- package/tests/assets/MergedNetLabelObstacles.test.input.json +154 -0
- package/tests/assets/OverlapAvoidanceStepSolver.test.input.json +682 -0
- package/tests/assets/SingleOverlapSolver.test.input.json +170 -0
- package/tests/assets/TraceCleanupSolver.test.input.json +216 -0
- package/tests/assets/TraceLabelOverlapAvoidanceSolver.test.input.json +424 -0
- package/tests/examples/__snapshots__/example25.snap.svg +3 -3
- package/tests/examples/__snapshots__/example26.snap.svg +3 -3
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.test.ts +18 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +243 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView01.snap.svg +223 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView02.snap.svg +205 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/__snapshots__/renderComparisonView03.snap.svg +184 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView01.test.ts +29 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView02.test.ts +27 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/renderComparisonView/renderComparisonView03.test.ts +39 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/MergedNetLabelObstacles.test.ts +14 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/OverlapAvoidanceStepSolver.test.ts +20 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/SingleOverlapSolver.test.ts +10 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/TraceCleanupSolver.test.ts +19 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/MergedNetLabelObstacles.snap.svg +140 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/OverlapAvoidanceStepSolver.snap.svg +216 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/SingleOverlapSolver.snap.svg +125 -0
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/sub-solver/__snapshots__/TraceCleanupSolver.snap.svg +119 -0
- /package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{countTurns.ts → sub-solvers/TraceCleanupSolver/countTurns.ts} +0 -0
- /package/lib/solvers/TraceLabelOverlapAvoidanceSolver/{tryConnectPoints.ts → sub-solvers/TraceCleanupSolver/tryConnectPoints.ts} +0 -0
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
2
|
-
import type { NetLabelPlacement } from "
|
|
3
|
-
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
|
-
import { getObstacleRects } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
2
|
+
import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
5
3
|
import { minimizeTurns } from "./turnMinimization"
|
|
4
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
5
|
+
import { getObstacleRects } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
6
6
|
|
|
7
7
|
export const minimizeTurnsWithFilteredLabels = ({
|
|
8
8
|
traces,
|
|
9
|
-
|
|
9
|
+
inputProblem,
|
|
10
10
|
allLabelPlacements,
|
|
11
11
|
mergedLabelNetIdMap,
|
|
12
12
|
paddingBuffer,
|
|
13
13
|
}: {
|
|
14
14
|
traces: SolvedTracePath[]
|
|
15
|
-
|
|
15
|
+
inputProblem: InputProblem
|
|
16
16
|
allLabelPlacements: NetLabelPlacement[]
|
|
17
17
|
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
18
18
|
paddingBuffer: number
|
|
19
19
|
}): SolvedTracePath[] | null => {
|
|
20
20
|
let changesMade = false
|
|
21
|
-
const obstacles = getObstacleRects(
|
|
21
|
+
const obstacles = getObstacleRects(inputProblem)
|
|
22
22
|
|
|
23
23
|
const newTraces = traces.map((trace) => {
|
|
24
24
|
const originalPath = trace.tracePath
|
|
@@ -2,7 +2,7 @@ import type { Point } from "graphics-debug"
|
|
|
2
2
|
import {
|
|
3
3
|
isHorizontal,
|
|
4
4
|
isVertical,
|
|
5
|
-
} from "
|
|
5
|
+
} from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
6
6
|
|
|
7
7
|
export const simplifyPath = (path: Point[]): Point[] => {
|
|
8
8
|
if (path.length < 3) return path
|
|
@@ -105,8 +105,6 @@ export const minimizeTurns = ({
|
|
|
105
105
|
|
|
106
106
|
if (!collidesWithObstacles && !collidesWithLabels) {
|
|
107
107
|
const newTurns = countTurns(testPath)
|
|
108
|
-
const turnsRemoved = stairEndIdx - startIdx - 1
|
|
109
|
-
|
|
110
108
|
optimizedPath = testPath
|
|
111
109
|
currentTurns = newTurns
|
|
112
110
|
improved = true
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Point } from "graphics-debug"
|
|
2
|
+
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
3
|
+
import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
4
|
+
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
5
|
+
import {
|
|
6
|
+
segmentIntersectsRect,
|
|
7
|
+
isVertical,
|
|
8
|
+
} from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
9
|
+
|
|
10
|
+
export const generateFourPointDetourCandidates = ({
|
|
11
|
+
initialTrace,
|
|
12
|
+
label,
|
|
13
|
+
labelBounds,
|
|
14
|
+
paddingBuffer,
|
|
15
|
+
detourCount,
|
|
16
|
+
}: {
|
|
17
|
+
initialTrace: SolvedTracePath
|
|
18
|
+
label: NetLabelPlacement
|
|
19
|
+
labelBounds: any
|
|
20
|
+
paddingBuffer: number
|
|
21
|
+
detourCount: number
|
|
22
|
+
}): Point[][] => {
|
|
23
|
+
let collidingSegIndex = -1
|
|
24
|
+
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
25
|
+
if (
|
|
26
|
+
segmentIntersectsRect(
|
|
27
|
+
initialTrace.tracePath[i],
|
|
28
|
+
initialTrace.tracePath[i + 1],
|
|
29
|
+
labelBounds,
|
|
30
|
+
)
|
|
31
|
+
) {
|
|
32
|
+
collidingSegIndex = i
|
|
33
|
+
break
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (collidingSegIndex === -1) return []
|
|
38
|
+
|
|
39
|
+
const pA = initialTrace.tracePath[collidingSegIndex]
|
|
40
|
+
const pB = initialTrace.tracePath[collidingSegIndex + 1]
|
|
41
|
+
|
|
42
|
+
if (!pA || !pB) return []
|
|
43
|
+
|
|
44
|
+
const candidateDetours: Point[][] = []
|
|
45
|
+
const paddedLabelBounds = getRectBounds(
|
|
46
|
+
label.center,
|
|
47
|
+
label.width,
|
|
48
|
+
label.height,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const effectivePadding = paddingBuffer + detourCount * paddingBuffer
|
|
52
|
+
|
|
53
|
+
if (isVertical(pA, pB)) {
|
|
54
|
+
const xCandidates = [
|
|
55
|
+
paddedLabelBounds.maxX + effectivePadding,
|
|
56
|
+
paddedLabelBounds.minX - effectivePadding,
|
|
57
|
+
]
|
|
58
|
+
for (const newX of xCandidates) {
|
|
59
|
+
candidateDetours.push(
|
|
60
|
+
pB.y > pA.y
|
|
61
|
+
? [
|
|
62
|
+
{ x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
63
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
64
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
65
|
+
{ x: pB.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
66
|
+
]
|
|
67
|
+
: [
|
|
68
|
+
{ x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
69
|
+
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
70
|
+
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
71
|
+
{ x: pB.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
72
|
+
],
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
const yCandidates = [
|
|
77
|
+
paddedLabelBounds.maxY + effectivePadding,
|
|
78
|
+
paddedLabelBounds.minY - effectivePadding,
|
|
79
|
+
]
|
|
80
|
+
for (const newY of yCandidates) {
|
|
81
|
+
candidateDetours.push(
|
|
82
|
+
pB.x > pA.x
|
|
83
|
+
? [
|
|
84
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
|
|
85
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
86
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
87
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pB.y },
|
|
88
|
+
]
|
|
89
|
+
: [
|
|
90
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
|
|
91
|
+
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
92
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
93
|
+
{ x: paddedLabelBounds.minX - effectivePadding, y: pB.y },
|
|
94
|
+
],
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return candidateDetours.map((detourPoints) => [
|
|
100
|
+
...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
|
|
101
|
+
...detourPoints,
|
|
102
|
+
...initialTrace.tracePath.slice(collidingSegIndex + 1),
|
|
103
|
+
])
|
|
104
|
+
}
|
|
@@ -1,32 +1,26 @@
|
|
|
1
1
|
import type { Point } from "@tscircuit/math-utils"
|
|
2
|
-
import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
3
2
|
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
|
-
import {
|
|
5
|
-
isPathCollidingWithObstacles,
|
|
6
|
-
isVertical,
|
|
7
|
-
segmentIntersectsRect,
|
|
8
|
-
} from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
9
|
-
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
10
|
-
import { simplifyPath } from "./simplifyPath"
|
|
11
3
|
|
|
12
|
-
export const
|
|
4
|
+
export const generateSnipAndReconnectCandidates = ({
|
|
13
5
|
initialTrace,
|
|
14
6
|
firstInsideIndex,
|
|
15
7
|
lastInsideIndex,
|
|
16
8
|
labelBounds,
|
|
17
|
-
|
|
9
|
+
paddingBuffer,
|
|
10
|
+
detourCount,
|
|
18
11
|
}: {
|
|
19
12
|
initialTrace: SolvedTracePath
|
|
20
13
|
firstInsideIndex: number
|
|
21
14
|
lastInsideIndex: number
|
|
22
15
|
labelBounds: any
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
paddingBuffer: number
|
|
17
|
+
detourCount: number
|
|
18
|
+
}): Point[][] => {
|
|
25
19
|
if (
|
|
26
20
|
firstInsideIndex <= 0 ||
|
|
27
21
|
lastInsideIndex >= initialTrace.tracePath.length - 1
|
|
28
22
|
) {
|
|
29
|
-
return
|
|
23
|
+
return []
|
|
30
24
|
}
|
|
31
25
|
|
|
32
26
|
const entryPoint = initialTrace.tracePath[firstInsideIndex - 1]
|
|
@@ -35,32 +29,19 @@ export const trySnipAndReconnect = ({
|
|
|
35
29
|
const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex)
|
|
36
30
|
const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1)
|
|
37
31
|
|
|
38
|
-
const
|
|
32
|
+
const allCandidateDetours: Point[][] = []
|
|
39
33
|
|
|
34
|
+
// Candidate type 1: simple elbow
|
|
40
35
|
if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
allCandidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }])
|
|
37
|
+
allCandidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }])
|
|
43
38
|
} else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (!isPathCollidingWithObstacles(simplified, obstacles)) {
|
|
47
|
-
return { ...initialTrace, tracePath: simplified }
|
|
48
|
-
}
|
|
39
|
+
// Candidate type 2: direct connection (if points are aligned)
|
|
40
|
+
allCandidateDetours.push([]) // No detour points needed
|
|
49
41
|
}
|
|
50
42
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const newFullPath = [...pathToEntry, ...detour, ...pathFromExit]
|
|
54
|
-
const simplified = simplifyPath(newFullPath)
|
|
55
|
-
|
|
56
|
-
if (!isPathCollidingWithObstacles(simplified, obstacles)) {
|
|
57
|
-
return { ...initialTrace, tracePath: simplified }
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
candidateDetours.length = 0
|
|
62
|
-
|
|
63
|
-
const buffer = 0.1
|
|
43
|
+
// Candidate type 3: routing around the label bounds
|
|
44
|
+
const buffer = paddingBuffer + detourCount * paddingBuffer
|
|
64
45
|
const leftX = labelBounds.minX - buffer
|
|
65
46
|
const rightX = labelBounds.maxX + buffer
|
|
66
47
|
const topY = labelBounds.maxY + buffer
|
|
@@ -71,7 +52,7 @@ export const trySnipAndReconnect = ({
|
|
|
71
52
|
entryPoint.x < labelBounds.maxX &&
|
|
72
53
|
exitPoint.x < labelBounds.maxX
|
|
73
54
|
) {
|
|
74
|
-
|
|
55
|
+
allCandidateDetours.push([
|
|
75
56
|
{ x: leftX, y: entryPoint.y },
|
|
76
57
|
{ x: leftX, y: exitPoint.y },
|
|
77
58
|
])
|
|
@@ -82,7 +63,7 @@ export const trySnipAndReconnect = ({
|
|
|
82
63
|
entryPoint.x > labelBounds.minX &&
|
|
83
64
|
exitPoint.x > labelBounds.minX
|
|
84
65
|
) {
|
|
85
|
-
|
|
66
|
+
allCandidateDetours.push([
|
|
86
67
|
{ x: rightX, y: entryPoint.y },
|
|
87
68
|
{ x: rightX, y: exitPoint.y },
|
|
88
69
|
])
|
|
@@ -93,7 +74,7 @@ export const trySnipAndReconnect = ({
|
|
|
93
74
|
entryPoint.y > labelBounds.minY &&
|
|
94
75
|
exitPoint.y > labelBounds.minY
|
|
95
76
|
) {
|
|
96
|
-
|
|
77
|
+
allCandidateDetours.push([
|
|
97
78
|
{ x: entryPoint.x, y: topY },
|
|
98
79
|
{ x: exitPoint.x, y: topY },
|
|
99
80
|
])
|
|
@@ -104,126 +85,15 @@ export const trySnipAndReconnect = ({
|
|
|
104
85
|
entryPoint.y < labelBounds.maxY &&
|
|
105
86
|
exitPoint.y < labelBounds.maxY
|
|
106
87
|
) {
|
|
107
|
-
|
|
88
|
+
allCandidateDetours.push([
|
|
108
89
|
{ x: entryPoint.x, y: bottomY },
|
|
109
90
|
{ x: exitPoint.x, y: bottomY },
|
|
110
91
|
])
|
|
111
92
|
}
|
|
112
93
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (!isPathCollidingWithObstacles(simplified, obstacles)) {
|
|
119
|
-
return { ...initialTrace, tracePath: simplified }
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return null
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export const tryFourPointDetour = ({
|
|
127
|
-
initialTrace,
|
|
128
|
-
label,
|
|
129
|
-
labelBounds,
|
|
130
|
-
obstacles,
|
|
131
|
-
paddingBuffer,
|
|
132
|
-
detourCount,
|
|
133
|
-
}: {
|
|
134
|
-
initialTrace: SolvedTracePath
|
|
135
|
-
label: NetLabelPlacement
|
|
136
|
-
labelBounds: any
|
|
137
|
-
obstacles: any[]
|
|
138
|
-
paddingBuffer: number
|
|
139
|
-
detourCount: number
|
|
140
|
-
}): SolvedTracePath | null => {
|
|
141
|
-
let collidingSegIndex = -1
|
|
142
|
-
for (let i = 0; i < initialTrace.tracePath.length - 1; i++) {
|
|
143
|
-
if (
|
|
144
|
-
segmentIntersectsRect(
|
|
145
|
-
initialTrace.tracePath[i],
|
|
146
|
-
initialTrace.tracePath[i + 1],
|
|
147
|
-
labelBounds,
|
|
148
|
-
)
|
|
149
|
-
) {
|
|
150
|
-
collidingSegIndex = i
|
|
151
|
-
break
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (collidingSegIndex === -1) return initialTrace
|
|
156
|
-
|
|
157
|
-
const pA = initialTrace.tracePath[collidingSegIndex]
|
|
158
|
-
const pB = initialTrace.tracePath[collidingSegIndex + 1]
|
|
159
|
-
|
|
160
|
-
if (!pA || !pB) return null
|
|
161
|
-
|
|
162
|
-
const candidateDetours: Point[][] = []
|
|
163
|
-
const paddedLabelBounds = getRectBounds(
|
|
164
|
-
label.center,
|
|
165
|
-
label.width,
|
|
166
|
-
label.height,
|
|
167
|
-
)
|
|
168
|
-
|
|
169
|
-
const effectivePadding = paddingBuffer + detourCount * paddingBuffer
|
|
170
|
-
|
|
171
|
-
if (isVertical(pA, pB)) {
|
|
172
|
-
const xCandidates = [
|
|
173
|
-
paddedLabelBounds.maxX + effectivePadding,
|
|
174
|
-
paddedLabelBounds.minX - effectivePadding,
|
|
175
|
-
]
|
|
176
|
-
for (const newX of xCandidates) {
|
|
177
|
-
candidateDetours.push(
|
|
178
|
-
pB.y > pA.y
|
|
179
|
-
? [
|
|
180
|
-
{ x: pA.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
181
|
-
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
182
|
-
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
183
|
-
{ x: pB.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
184
|
-
]
|
|
185
|
-
: [
|
|
186
|
-
{ x: pA.x, y: paddedLabelBounds.maxY + effectivePadding },
|
|
187
|
-
{ x: newX, y: paddedLabelBounds.maxY + effectivePadding },
|
|
188
|
-
{ x: newX, y: paddedLabelBounds.minY - effectivePadding },
|
|
189
|
-
{ x: pB.x, y: paddedLabelBounds.minY - effectivePadding },
|
|
190
|
-
],
|
|
191
|
-
)
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
const yCandidates = [
|
|
195
|
-
paddedLabelBounds.maxY + effectivePadding,
|
|
196
|
-
paddedLabelBounds.minY - effectivePadding,
|
|
197
|
-
]
|
|
198
|
-
for (const newY of yCandidates) {
|
|
199
|
-
candidateDetours.push(
|
|
200
|
-
pB.x > pA.x
|
|
201
|
-
? [
|
|
202
|
-
{ x: paddedLabelBounds.minX - effectivePadding, y: pA.y },
|
|
203
|
-
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
204
|
-
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
205
|
-
{ x: paddedLabelBounds.maxX + effectivePadding, y: pB.y },
|
|
206
|
-
]
|
|
207
|
-
: [
|
|
208
|
-
{ x: paddedLabelBounds.maxX + effectivePadding, y: pA.y },
|
|
209
|
-
{ x: paddedLabelBounds.maxX + effectivePadding, y: newY },
|
|
210
|
-
{ x: paddedLabelBounds.minX - effectivePadding, y: newY },
|
|
211
|
-
{ x: paddedLabelBounds.minX - effectivePadding, y: pB.y },
|
|
212
|
-
],
|
|
213
|
-
)
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
for (const detourPoints of candidateDetours) {
|
|
218
|
-
const finalPath = [
|
|
219
|
-
...initialTrace.tracePath.slice(0, collidingSegIndex + 1),
|
|
220
|
-
...detourPoints,
|
|
221
|
-
...initialTrace.tracePath.slice(collidingSegIndex + 1),
|
|
222
|
-
]
|
|
223
|
-
const simplifiedFinalPath = simplifyPath(finalPath)
|
|
224
|
-
if (!isPathCollidingWithObstacles(simplifiedFinalPath, obstacles)) {
|
|
225
|
-
return { ...initialTrace, tracePath: simplifiedFinalPath }
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
return null
|
|
94
|
+
return allCandidateDetours.map((detour) => [
|
|
95
|
+
...pathToEntry,
|
|
96
|
+
...detour,
|
|
97
|
+
...pathFromExit,
|
|
98
|
+
])
|
|
229
99
|
}
|