@tscircuit/schematic-trace-solver 0.0.36 → 0.0.38
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 +26 -1
- package/dist/index.js +936 -23
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts +15 -3
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions.ts +17 -0
- package/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/pathOps.ts +31 -12
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +40 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +247 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/balanceLShapes.ts +207 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/countTurns.ts +18 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts +38 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/hasCollisions.ts +26 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/index.ts +1 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/rerouteCollidingTrace.ts +73 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/simplifyPath.ts +41 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/trySnipAndReconnect.ts +229 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/turnMinimization.ts +305 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/violation.ts +25 -0
- package/package.json +1 -1
- package/site/examples/example20.page.tsx +103 -0
- package/site/examples/example21.page.tsx +177 -0
- package/site/examples/example22.page.tsx +108 -0
- package/site/examples/example23.page.tsx +137 -0
- package/site/examples/example24.page.tsx +128 -0
- package/tests/examples/__snapshots__/example16.snap.svg +27 -27
- package/tests/examples/__snapshots__/example21.snap.svg +272 -0
- package/tests/examples/__snapshots__/example22.snap.svg +137 -0
- package/tests/examples/__snapshots__/example23.snap.svg +137 -0
- package/tests/examples/__snapshots__/example24.snap.svg +143 -0
- package/tests/examples/__snapshots__/example25.snap.svg +165 -0
- package/tests/examples/__snapshots__/example26.snap.svg +157 -0
- package/tests/examples/example21.test.tsx +183 -0
- package/tests/examples/example22.test.tsx +109 -0
- package/tests/examples/example23.test.tsx +109 -0
- package/tests/examples/example24.test.tsx +114 -0
- package/tests/examples/example25.test.tsx +143 -0
- package/tests/examples/example26.test.tsx +134 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
2
|
+
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
3
|
+
import { segmentIntersectsRect } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
|
|
4
|
+
import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
5
|
+
|
|
6
|
+
export interface TraceLabelOverlap {
|
|
7
|
+
trace: SolvedTracePath
|
|
8
|
+
label: NetLabelPlacement
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const detectTraceLabelOverlap = (
|
|
12
|
+
traces: SolvedTracePath[],
|
|
13
|
+
netLabels: NetLabelPlacement[],
|
|
14
|
+
): TraceLabelOverlap[] => {
|
|
15
|
+
const overlaps: TraceLabelOverlap[] = []
|
|
16
|
+
|
|
17
|
+
for (const trace of traces) {
|
|
18
|
+
for (const label of netLabels) {
|
|
19
|
+
const labelBounds = getRectBounds(label.center, label.width, label.height)
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < trace.tracePath.length - 1; i++) {
|
|
22
|
+
const p1 = trace.tracePath[i]
|
|
23
|
+
const p2 = trace.tracePath[i + 1]
|
|
24
|
+
|
|
25
|
+
if (segmentIntersectsRect(p1, p2, labelBounds)) {
|
|
26
|
+
// Check if the trace and label belong to the same net.
|
|
27
|
+
// If so, it's a self-attachment, not a collision.
|
|
28
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
29
|
+
break // Move to the next label
|
|
30
|
+
}
|
|
31
|
+
overlaps.push({ trace, label })
|
|
32
|
+
break // Move to the next label
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return overlaps
|
|
38
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Point } from "graphics-debug"
|
|
2
|
+
import {
|
|
3
|
+
isVertical,
|
|
4
|
+
isHorizontal,
|
|
5
|
+
segmentIntersectsRect,
|
|
6
|
+
} from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
7
|
+
|
|
8
|
+
export const hasCollisions = (
|
|
9
|
+
pathSegments: Point[],
|
|
10
|
+
obstacles: any[],
|
|
11
|
+
): boolean => {
|
|
12
|
+
// Check each segment of the path
|
|
13
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
14
|
+
const p1 = pathSegments[i]
|
|
15
|
+
const p2 = pathSegments[i + 1]
|
|
16
|
+
|
|
17
|
+
// Check collision with each obstacle
|
|
18
|
+
for (const obstacle of obstacles) {
|
|
19
|
+
if (segmentIntersectsRect(p1, p2, obstacle)) {
|
|
20
|
+
return true
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./TraceLabelOverlapAvoidanceSolver"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { SolvedTracePath } from "../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
2
|
+
import type { NetLabelPlacement } from "../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
3
|
+
import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
4
|
+
import { getObstacleRects } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/rect"
|
|
5
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
6
|
+
import { findTraceViolationZone } from "./violation"
|
|
7
|
+
import { tryFourPointDetour, trySnipAndReconnect } from "./trySnipAndReconnect"
|
|
8
|
+
import { simplifyPath } from "./simplifyPath"
|
|
9
|
+
|
|
10
|
+
export const rerouteCollidingTrace = ({
|
|
11
|
+
trace,
|
|
12
|
+
label,
|
|
13
|
+
problem,
|
|
14
|
+
paddingBuffer,
|
|
15
|
+
detourCount,
|
|
16
|
+
}: {
|
|
17
|
+
trace: SolvedTracePath
|
|
18
|
+
label: NetLabelPlacement
|
|
19
|
+
problem: InputProblem
|
|
20
|
+
paddingBuffer: number
|
|
21
|
+
detourCount: number
|
|
22
|
+
}): SolvedTracePath => {
|
|
23
|
+
const initialTrace = { ...trace, tracePath: simplifyPath(trace.tracePath) }
|
|
24
|
+
|
|
25
|
+
if (trace.globalConnNetId === label.globalConnNetId) {
|
|
26
|
+
return initialTrace
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const obstacles = getObstacleRects(problem)
|
|
30
|
+
const labelPadding = paddingBuffer
|
|
31
|
+
const labelBoundsRaw = getRectBounds(label.center, label.width, label.height)
|
|
32
|
+
const labelBounds = {
|
|
33
|
+
minX: labelBoundsRaw.minX - labelPadding,
|
|
34
|
+
minY: labelBoundsRaw.minY - labelPadding,
|
|
35
|
+
maxX: labelBoundsRaw.maxX + labelPadding,
|
|
36
|
+
maxY: labelBoundsRaw.maxY + labelPadding,
|
|
37
|
+
chipId: `netlabel-${label.netId}`,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const fourPointResult = tryFourPointDetour({
|
|
41
|
+
initialTrace,
|
|
42
|
+
label,
|
|
43
|
+
labelBounds,
|
|
44
|
+
obstacles,
|
|
45
|
+
paddingBuffer,
|
|
46
|
+
detourCount,
|
|
47
|
+
})
|
|
48
|
+
if (fourPointResult) {
|
|
49
|
+
initialTrace.tracePath = fourPointResult.tracePath
|
|
50
|
+
}
|
|
51
|
+
const { firstInsideIndex, lastInsideIndex } = findTraceViolationZone(
|
|
52
|
+
initialTrace.tracePath,
|
|
53
|
+
labelBounds,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
const snipReconnectResult = trySnipAndReconnect({
|
|
57
|
+
initialTrace,
|
|
58
|
+
firstInsideIndex,
|
|
59
|
+
lastInsideIndex,
|
|
60
|
+
labelBounds,
|
|
61
|
+
obstacles,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
if (snipReconnectResult) {
|
|
65
|
+
return snipReconnectResult
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (fourPointResult) {
|
|
69
|
+
return fourPointResult
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return initialTrace
|
|
73
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Point } from "graphics-debug"
|
|
2
|
+
import {
|
|
3
|
+
isHorizontal,
|
|
4
|
+
isVertical,
|
|
5
|
+
} from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/collisions"
|
|
6
|
+
|
|
7
|
+
export const simplifyPath = (path: Point[]): Point[] => {
|
|
8
|
+
if (path.length < 3) return path
|
|
9
|
+
const newPath: Point[] = [path[0]]
|
|
10
|
+
for (let i = 1; i < path.length - 1; i++) {
|
|
11
|
+
const p1 = newPath[newPath.length - 1]
|
|
12
|
+
const p2 = path[i]
|
|
13
|
+
const p3 = path[i + 1]
|
|
14
|
+
if (
|
|
15
|
+
(isVertical(p1, p2) && isVertical(p2, p3)) ||
|
|
16
|
+
(isHorizontal(p1, p2) && isHorizontal(p2, p3))
|
|
17
|
+
) {
|
|
18
|
+
continue
|
|
19
|
+
}
|
|
20
|
+
newPath.push(p2)
|
|
21
|
+
}
|
|
22
|
+
newPath.push(path[path.length - 1])
|
|
23
|
+
|
|
24
|
+
if (newPath.length < 3) return newPath
|
|
25
|
+
const finalPath: Point[] = [newPath[0]]
|
|
26
|
+
for (let i = 1; i < newPath.length - 1; i++) {
|
|
27
|
+
const p1 = finalPath[finalPath.length - 1]
|
|
28
|
+
const p2 = newPath[i]
|
|
29
|
+
const p3 = newPath[i + 1]
|
|
30
|
+
if (
|
|
31
|
+
(isVertical(p1, p2) && isVertical(p2, p3)) ||
|
|
32
|
+
(isHorizontal(p1, p2) && isHorizontal(p2, p3))
|
|
33
|
+
) {
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
finalPath.push(p2)
|
|
37
|
+
}
|
|
38
|
+
finalPath.push(newPath[newPath.length - 1])
|
|
39
|
+
|
|
40
|
+
return finalPath
|
|
41
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import type { Point } from "@tscircuit/math-utils"
|
|
2
|
+
import { getRectBounds } from "../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
3
|
+
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
|
+
|
|
12
|
+
export const trySnipAndReconnect = ({
|
|
13
|
+
initialTrace,
|
|
14
|
+
firstInsideIndex,
|
|
15
|
+
lastInsideIndex,
|
|
16
|
+
labelBounds,
|
|
17
|
+
obstacles,
|
|
18
|
+
}: {
|
|
19
|
+
initialTrace: SolvedTracePath
|
|
20
|
+
firstInsideIndex: number
|
|
21
|
+
lastInsideIndex: number
|
|
22
|
+
labelBounds: any
|
|
23
|
+
obstacles: any[]
|
|
24
|
+
}): SolvedTracePath | null => {
|
|
25
|
+
if (
|
|
26
|
+
firstInsideIndex <= 0 ||
|
|
27
|
+
lastInsideIndex >= initialTrace.tracePath.length - 1
|
|
28
|
+
) {
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const entryPoint = initialTrace.tracePath[firstInsideIndex - 1]
|
|
33
|
+
const exitPoint = initialTrace.tracePath[lastInsideIndex + 1]
|
|
34
|
+
|
|
35
|
+
const pathToEntry = initialTrace.tracePath.slice(0, firstInsideIndex)
|
|
36
|
+
const pathFromExit = initialTrace.tracePath.slice(lastInsideIndex + 1)
|
|
37
|
+
|
|
38
|
+
const candidateDetours: Point[][] = []
|
|
39
|
+
|
|
40
|
+
if (entryPoint.x !== exitPoint.x && entryPoint.y !== exitPoint.y) {
|
|
41
|
+
candidateDetours.push([{ x: exitPoint.x, y: entryPoint.y }])
|
|
42
|
+
candidateDetours.push([{ x: entryPoint.x, y: exitPoint.y }])
|
|
43
|
+
} else if (entryPoint.x === exitPoint.x || entryPoint.y === exitPoint.y) {
|
|
44
|
+
const newPath = [...pathToEntry, ...pathFromExit]
|
|
45
|
+
const simplified = simplifyPath(newPath)
|
|
46
|
+
if (!isPathCollidingWithObstacles(simplified, obstacles)) {
|
|
47
|
+
return { ...initialTrace, tracePath: simplified }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (let i = 0; i < candidateDetours.length; i++) {
|
|
52
|
+
const detour = candidateDetours[i]
|
|
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
|
|
64
|
+
const leftX = labelBounds.minX - buffer
|
|
65
|
+
const rightX = labelBounds.maxX + buffer
|
|
66
|
+
const topY = labelBounds.maxY + buffer
|
|
67
|
+
const bottomY = labelBounds.minY - buffer
|
|
68
|
+
|
|
69
|
+
if (
|
|
70
|
+
(entryPoint.x <= labelBounds.minX || exitPoint.x <= labelBounds.minX) &&
|
|
71
|
+
entryPoint.x < labelBounds.maxX &&
|
|
72
|
+
exitPoint.x < labelBounds.maxX
|
|
73
|
+
) {
|
|
74
|
+
candidateDetours.push([
|
|
75
|
+
{ x: leftX, y: entryPoint.y },
|
|
76
|
+
{ x: leftX, y: exitPoint.y },
|
|
77
|
+
])
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (
|
|
81
|
+
(entryPoint.x >= labelBounds.maxX || exitPoint.x >= labelBounds.maxX) &&
|
|
82
|
+
entryPoint.x > labelBounds.minX &&
|
|
83
|
+
exitPoint.x > labelBounds.minX
|
|
84
|
+
) {
|
|
85
|
+
candidateDetours.push([
|
|
86
|
+
{ x: rightX, y: entryPoint.y },
|
|
87
|
+
{ x: rightX, y: exitPoint.y },
|
|
88
|
+
])
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
(entryPoint.y >= labelBounds.maxY || exitPoint.y >= labelBounds.maxY) &&
|
|
93
|
+
entryPoint.y > labelBounds.minY &&
|
|
94
|
+
exitPoint.y > labelBounds.minY
|
|
95
|
+
) {
|
|
96
|
+
candidateDetours.push([
|
|
97
|
+
{ x: entryPoint.x, y: topY },
|
|
98
|
+
{ x: exitPoint.x, y: topY },
|
|
99
|
+
])
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
(entryPoint.y <= labelBounds.minY || exitPoint.y <= labelBounds.minY) &&
|
|
104
|
+
entryPoint.y < labelBounds.maxY &&
|
|
105
|
+
exitPoint.y < labelBounds.maxY
|
|
106
|
+
) {
|
|
107
|
+
candidateDetours.push([
|
|
108
|
+
{ x: entryPoint.x, y: bottomY },
|
|
109
|
+
{ x: exitPoint.x, y: bottomY },
|
|
110
|
+
])
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i < candidateDetours.length; i++) {
|
|
114
|
+
const detour = candidateDetours[i]
|
|
115
|
+
const newFullPath = [...pathToEntry, ...detour, ...pathFromExit]
|
|
116
|
+
const simplified = simplifyPath(newFullPath)
|
|
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
|
|
229
|
+
}
|