@tscircuit/schematic-trace-solver 0.0.44 → 0.0.46
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/.github/workflows/bun-formatcheck.yml +1 -1
- package/.github/workflows/bun-pver-release.yml +1 -1
- package/.github/workflows/bun-test.yml +1 -1
- package/.github/workflows/bun-typecheck.yml +1 -1
- package/dist/index.d.ts +43 -8
- package/dist/index.js +1109 -185
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +0 -1
- package/lib/solvers/TraceCleanupSolver/TraceCleanupSolver.ts +83 -36
- package/lib/solvers/TraceCleanupSolver/hasCollisions.ts +16 -4
- package/lib/solvers/TraceCleanupSolver/is4PointRectangle.ts +17 -0
- package/lib/solvers/TraceCleanupSolver/isSegmentAnEndpointSegment.ts +36 -0
- package/lib/solvers/TraceCleanupSolver/mergeGraphicsObjects.ts +28 -0
- package/lib/solvers/TraceCleanupSolver/minimizeTurnsWithFilteredLabels.ts +18 -1
- package/lib/solvers/TraceCleanupSolver/recognizeStairStepPattern.ts +56 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/UntangleTraceSubsolver.ts +370 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/findAllLShapedTurns.ts +48 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/findIntersectionsWithObstacles.ts +36 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/generateLShapeRerouteCandidates.ts +107 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/generateRectangleCandidates.ts +55 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/getTraceObstacles.ts +25 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/isPathColliding.ts +58 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCandidates.ts +33 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/visualizeCollision.ts +20 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/visualizeIntersectionPoints.ts +26 -0
- package/lib/solvers/TraceCleanupSolver/sub-solver/visualizeLSapes.ts +33 -0
- package/lib/solvers/TraceCleanupSolver/turnMinimization.ts +50 -56
- package/lib/solvers/TraceCleanupSolver/visualizeTightRectangle.ts +24 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/TraceLabelOverlapAvoidanceSolver.ts +17 -3
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/detectTraceLabelOverlap.ts +26 -11
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/LabelMergingSolver.ts +95 -75
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/filterLabelsAtTraceEdges.ts +74 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/groupLabelsByChipAndOrientation.ts +45 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/LabelMergingSolver/mergeLabelGroup.ts +71 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +150 -27
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/isPointInsideLabel.ts +23 -0
- package/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/visualizeDecomposition.ts +54 -0
- package/package.json +1 -1
- package/tests/examples/__snapshots__/example29.snap.svg +6 -6
- package/tests/solvers/TraceLabelOverlapAvoidanceSolver/__snapshots__/TraceLabelOverlapAvoidanceSolver.snap.svg +8 -8
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
2
|
+
import type { SolvedTracePath } from "../../../SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
3
|
+
import type { Point } from "graphics-debug"
|
|
4
|
+
import { distance } from "@tscircuit/math-utils"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Filters a list of labels, returning only those that are physically located
|
|
8
|
+
* near the start or end point of any trace.
|
|
9
|
+
*
|
|
10
|
+
* This is hack so we do not need to know the position of ports
|
|
11
|
+
* and using only the trace geometry. we find out which labels are
|
|
12
|
+
* at/near port of chips and only keep those for merging.
|
|
13
|
+
*/
|
|
14
|
+
export const filterLabelsAtTraceEdges = ({
|
|
15
|
+
labels,
|
|
16
|
+
traces,
|
|
17
|
+
distanceThreshold = 0.5, // Example threshold
|
|
18
|
+
}: {
|
|
19
|
+
labels: NetLabelPlacement[]
|
|
20
|
+
traces: SolvedTracePath[]
|
|
21
|
+
distanceThreshold?: number
|
|
22
|
+
}): NetLabelPlacement[] => {
|
|
23
|
+
// 1. Group traces by their net ID for efficient lookup
|
|
24
|
+
const tracesByNetId = new Map<string, SolvedTracePath[]>()
|
|
25
|
+
if (!traces) {
|
|
26
|
+
throw new Error("No traces provided to filterLabelsAtTraceEdges")
|
|
27
|
+
}
|
|
28
|
+
for (const trace of traces) {
|
|
29
|
+
if (!trace.globalConnNetId) continue
|
|
30
|
+
if (!tracesByNetId.has(trace.globalConnNetId)) {
|
|
31
|
+
tracesByNetId.set(trace.globalConnNetId, [])
|
|
32
|
+
}
|
|
33
|
+
tracesByNetId.get(trace.globalConnNetId)!.push(trace)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const filteredLabels: NetLabelPlacement[] = []
|
|
37
|
+
|
|
38
|
+
// 2. Iterate through labels and check proximity against only relevant traces
|
|
39
|
+
for (const label of labels) {
|
|
40
|
+
// Check if it's a port-only label (no associated MSP connection pairs)
|
|
41
|
+
if (label.mspConnectionPairIds.length === 0) {
|
|
42
|
+
filteredLabels.push(label)
|
|
43
|
+
continue // Skip trace-edge checks for port-only labels
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const relevantTraces = tracesByNetId.get(label.globalConnNetId)
|
|
47
|
+
let isNearTraceEdge = false
|
|
48
|
+
|
|
49
|
+
if (!relevantTraces || relevantTraces.length === 0) {
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const trace of relevantTraces) {
|
|
54
|
+
if (trace.tracePath.length === 0) continue
|
|
55
|
+
|
|
56
|
+
const startPoint = trace.tracePath[0]
|
|
57
|
+
const endPoint = trace.tracePath[trace.tracePath.length - 1]
|
|
58
|
+
|
|
59
|
+
const startDist = distance(label.center, startPoint)
|
|
60
|
+
const endDist = distance(label.center, endPoint)
|
|
61
|
+
|
|
62
|
+
if (startDist <= distanceThreshold || endDist <= distanceThreshold) {
|
|
63
|
+
isNearTraceEdge = true
|
|
64
|
+
break // Found a nearby edge, no need to check other traces for this label
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (isNearTraceEdge) {
|
|
69
|
+
filteredLabels.push(label)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return filteredLabels
|
|
74
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
2
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Groups NetLabelPlacement objects by their associated chip ID and orientation.
|
|
6
|
+
* Labels are grouped if they belong to the same chip and are positioned
|
|
7
|
+
* with the same orientation relative to that chip.
|
|
8
|
+
*
|
|
9
|
+
* @param labels An array of NetLabelPlacement objects to be grouped.
|
|
10
|
+
* @param chips An array of Chip objects from the InputProblem. (Currently not directly used for grouping logic, but part of the signature).
|
|
11
|
+
* @returns A record where keys are in the format "chipId-orientation" (e.g., "U1-left")
|
|
12
|
+
* and values are arrays of NetLabelPlacement objects belonging to that group.
|
|
13
|
+
*/
|
|
14
|
+
export const groupLabelsByChipAndOrientation = ({
|
|
15
|
+
labels,
|
|
16
|
+
chips,
|
|
17
|
+
}: {
|
|
18
|
+
labels: NetLabelPlacement[]
|
|
19
|
+
chips: InputProblem["chips"]
|
|
20
|
+
}): Record<string, NetLabelPlacement[]> => {
|
|
21
|
+
const groupedLabels: Record<string, NetLabelPlacement[]> = {}
|
|
22
|
+
|
|
23
|
+
for (const label of labels) {
|
|
24
|
+
if (label.pinIds.length === 0) {
|
|
25
|
+
// Labels without pinIds cannot be associated with a chip and orientation for merging
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Extract chipId from the first pinId (e.g., "U1.1" -> "U1")
|
|
30
|
+
const chipId = label.pinIds[0].split(".")[0]
|
|
31
|
+
if (!chipId) {
|
|
32
|
+
// Should not happen if pinIds are well-formed, but good to guard
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const key = `${chipId}-${label.orientation}`
|
|
37
|
+
|
|
38
|
+
if (!groupedLabels[key]) {
|
|
39
|
+
groupedLabels[key] = []
|
|
40
|
+
}
|
|
41
|
+
groupedLabels[key].push(label)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return groupedLabels
|
|
45
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { NetLabelPlacement } from "../../../NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
2
|
+
import { getRectBounds } from "../../../NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
3
|
+
import type { Point } from "graphics-debug" // Assuming Point is from graphics-debug or similar
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Merges a group of NetLabelPlacement objects into a single, larger NetLabelPlacement.
|
|
7
|
+
* It calculates a new bounding box that encompasses all labels in the group,
|
|
8
|
+
* aggregates unique pin and MSP connection pair IDs, and creates a synthetic
|
|
9
|
+
* merged label.
|
|
10
|
+
*
|
|
11
|
+
* @param group An array of NetLabelPlacement objects to be merged. Assumes the group is not empty.
|
|
12
|
+
* @param groupKey A string key representing the group (e.g., "chip1-left"), used for generating the merged label's globalConnNetId.
|
|
13
|
+
* @returns An object containing the newly created merged NetLabelPlacement and a Set of the original globalConnNetIds that were merged.
|
|
14
|
+
* @throws Error if the input group is empty.
|
|
15
|
+
*/
|
|
16
|
+
export const mergeLabelGroup = (
|
|
17
|
+
group: NetLabelPlacement[],
|
|
18
|
+
groupKey: string,
|
|
19
|
+
): {
|
|
20
|
+
mergedLabel: NetLabelPlacement
|
|
21
|
+
originalNetIds: Set<string>
|
|
22
|
+
} => {
|
|
23
|
+
if (group.length === 0) {
|
|
24
|
+
throw new Error("Cannot merge an empty group of labels.")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let minX = Infinity
|
|
28
|
+
let minY = Infinity
|
|
29
|
+
let maxX = -Infinity
|
|
30
|
+
let maxY = -Infinity
|
|
31
|
+
|
|
32
|
+
const allPinIds = new Set<string>()
|
|
33
|
+
const allMspConnectionPairIds = new Set<string>()
|
|
34
|
+
const originalNetIds = new Set<string>()
|
|
35
|
+
|
|
36
|
+
for (const label of group) {
|
|
37
|
+
const bounds = getRectBounds(label.center, label.width, label.height)
|
|
38
|
+
minX = Math.min(minX, bounds.minX)
|
|
39
|
+
minY = Math.min(minY, bounds.minY)
|
|
40
|
+
maxX = Math.max(maxX, bounds.maxX)
|
|
41
|
+
maxY = Math.max(maxY, bounds.maxY)
|
|
42
|
+
|
|
43
|
+
label.pinIds.forEach((id) => allPinIds.add(id))
|
|
44
|
+
label.mspConnectionPairIds.forEach((id) => allMspConnectionPairIds.add(id))
|
|
45
|
+
originalNetIds.add(label.globalConnNetId)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const newWidth = maxX - minX
|
|
49
|
+
const newHeight = maxY - minY
|
|
50
|
+
const newCenter: Point = { x: minX + newWidth / 2, y: minY + newHeight / 2 }
|
|
51
|
+
|
|
52
|
+
// Use the first label as a template for properties that are consistent across the group
|
|
53
|
+
// like orientation.
|
|
54
|
+
const template = group[0]!
|
|
55
|
+
const syntheticId = `merged-group-${groupKey}`
|
|
56
|
+
|
|
57
|
+
const mergedLabel: NetLabelPlacement = {
|
|
58
|
+
...template, // Copy common properties
|
|
59
|
+
globalConnNetId: syntheticId,
|
|
60
|
+
center: newCenter,
|
|
61
|
+
width: newWidth,
|
|
62
|
+
height: newHeight,
|
|
63
|
+
pinIds: Array.from(allPinIds),
|
|
64
|
+
mspConnectionPairIds: Array.from(allMspConnectionPairIds),
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
mergedLabel,
|
|
69
|
+
originalNetIds,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -6,14 +6,17 @@ import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/
|
|
|
6
6
|
import type { InputProblem } from "lib/types/InputProblem"
|
|
7
7
|
import { detectTraceLabelOverlap } from "../../detectTraceLabelOverlap"
|
|
8
8
|
import { SingleOverlapSolver } from "../SingleOverlapSolver/SingleOverlapSolver"
|
|
9
|
+
import { isPointInsideLabel } from "./isPointInsideLabel"
|
|
10
|
+
import { visualizeDecomposition } from "./visualizeDecomposition"
|
|
9
11
|
|
|
10
12
|
type Overlap = ReturnType<typeof detectTraceLabelOverlap>[0]
|
|
11
13
|
|
|
12
14
|
// Define a type for the input of the internal overlap solver to avoid conflicts
|
|
13
|
-
interface OverlapCollectionSolverInput {
|
|
15
|
+
export interface OverlapCollectionSolverInput {
|
|
14
16
|
inputProblem: InputProblem
|
|
15
17
|
traces: SolvedTracePath[]
|
|
16
|
-
|
|
18
|
+
initialNetLabelPlacements: NetLabelPlacement[]
|
|
19
|
+
mergedNetLabelPlacements: NetLabelPlacement[]
|
|
17
20
|
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -23,7 +26,8 @@ interface OverlapCollectionSolverInput {
|
|
|
23
26
|
*/
|
|
24
27
|
export class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
25
28
|
inputProblem: InputProblem
|
|
26
|
-
|
|
29
|
+
initialNetLabelPlacements: NetLabelPlacement[]
|
|
30
|
+
mergedNetLabelPlacements: NetLabelPlacement[]
|
|
27
31
|
mergedLabelNetIdMap: Record<string, Set<string>>
|
|
28
32
|
|
|
29
33
|
allTraces: SolvedTracePath[]
|
|
@@ -36,15 +40,21 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
|
36
40
|
private overlapQueue: Overlap[] = []
|
|
37
41
|
private recentlyFailed: Set<string> = new Set()
|
|
38
42
|
|
|
43
|
+
private currentlyProcessingOverlap: Overlap | null = null
|
|
44
|
+
private decomposedChildLabels: NetLabelPlacement[] | null = null
|
|
45
|
+
|
|
39
46
|
constructor(solverInput: OverlapCollectionSolverInput) {
|
|
40
47
|
super()
|
|
41
48
|
this.inputProblem = solverInput.inputProblem
|
|
42
|
-
this.
|
|
49
|
+
this.initialNetLabelPlacements = solverInput.initialNetLabelPlacements
|
|
50
|
+
this.mergedNetLabelPlacements = solverInput.mergedNetLabelPlacements
|
|
43
51
|
this.mergedLabelNetIdMap = solverInput.mergedLabelNetIdMap
|
|
44
52
|
this.allTraces = [...solverInput.traces]
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
override _step() {
|
|
56
|
+
this.currentlyProcessingOverlap = null
|
|
57
|
+
this.decomposedChildLabels = null
|
|
48
58
|
if (this.activeSubSolver) {
|
|
49
59
|
this.activeSubSolver.step()
|
|
50
60
|
|
|
@@ -65,19 +75,14 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
|
65
75
|
const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}`
|
|
66
76
|
this.recentlyFailed.add(overlapId)
|
|
67
77
|
this.activeSubSolver = null
|
|
78
|
+
} else {
|
|
68
79
|
}
|
|
69
80
|
return
|
|
70
81
|
}
|
|
71
82
|
|
|
72
|
-
const overlaps = detectTraceLabelOverlap(
|
|
73
|
-
this.allTraces,
|
|
74
|
-
this.
|
|
75
|
-
).filter((o) => {
|
|
76
|
-
const originalNetIds = this.mergedLabelNetIdMap[o.label.globalConnNetId]
|
|
77
|
-
if (originalNetIds) {
|
|
78
|
-
return !originalNetIds.has(o.trace.globalConnNetId)
|
|
79
|
-
}
|
|
80
|
-
return o.trace.globalConnNetId !== o.label.globalConnNetId
|
|
83
|
+
const overlaps = detectTraceLabelOverlap({
|
|
84
|
+
traces: this.allTraces,
|
|
85
|
+
netLabels: this.mergedNetLabelPlacements,
|
|
81
86
|
})
|
|
82
87
|
|
|
83
88
|
if (overlaps.length === 0) {
|
|
@@ -85,6 +90,7 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
|
85
90
|
return
|
|
86
91
|
}
|
|
87
92
|
|
|
93
|
+
// Filter out overlaps that have recently failed to avoid infinite loops
|
|
88
94
|
const nonFailedOverlaps = overlaps.filter((o) => {
|
|
89
95
|
const overlapId = `${o.trace.mspPairId}-${o.label.globalConnNetId}`
|
|
90
96
|
return !this.recentlyFailed.has(overlapId)
|
|
@@ -98,24 +104,104 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
|
98
104
|
this.overlapQueue = nonFailedOverlaps
|
|
99
105
|
|
|
100
106
|
const nextOverlap = this.overlapQueue.shift()
|
|
107
|
+
this.currentlyProcessingOverlap = nextOverlap ?? null
|
|
101
108
|
|
|
102
109
|
if (nextOverlap) {
|
|
103
110
|
const traceToFix = this.allTraces.find(
|
|
104
111
|
(t) => t.mspPairId === nextOverlap.trace.mspPairId,
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
)!
|
|
113
|
+
const labelToAvoid = nextOverlap.label
|
|
114
|
+
const traceStartPoint = traceToFix.tracePath[0]
|
|
115
|
+
|
|
116
|
+
const originalNetIds =
|
|
117
|
+
this.mergedLabelNetIdMap[labelToAvoid.globalConnNetId]
|
|
118
|
+
const isSelfOverlap = originalNetIds?.has(traceToFix.globalConnNetId)
|
|
119
|
+
|
|
120
|
+
if (isSelfOverlap) {
|
|
121
|
+
const childLabels = this.initialNetLabelPlacements.filter((l) =>
|
|
122
|
+
originalNetIds.has(l.globalConnNetId),
|
|
123
|
+
)
|
|
124
|
+
this.decomposedChildLabels = childLabels
|
|
125
|
+
let actualOverlapLabel: NetLabelPlacement | null = null
|
|
126
|
+
|
|
127
|
+
for (const childLabel of childLabels) {
|
|
128
|
+
const overlapsWithChild = detectTraceLabelOverlap({
|
|
129
|
+
traces: [traceToFix],
|
|
130
|
+
netLabels: [childLabel],
|
|
131
|
+
})
|
|
132
|
+
if (overlapsWithChild.length > 0) {
|
|
133
|
+
actualOverlapLabel = childLabel
|
|
134
|
+
break
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (actualOverlapLabel) {
|
|
139
|
+
const labelId = actualOverlapLabel.globalConnNetId
|
|
140
|
+
const detourCount = this.detourCountByLabel[labelId] || 0
|
|
141
|
+
this.detourCountByLabel[labelId] = detourCount + 1
|
|
142
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
143
|
+
trace: traceToFix,
|
|
144
|
+
label: actualOverlapLabel,
|
|
145
|
+
problem: this.inputProblem,
|
|
146
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
147
|
+
detourCount,
|
|
148
|
+
})
|
|
149
|
+
} else {
|
|
150
|
+
const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`
|
|
151
|
+
this.recentlyFailed.add(overlapId)
|
|
152
|
+
}
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (
|
|
157
|
+
originalNetIds &&
|
|
158
|
+
isPointInsideLabel({ point: traceStartPoint, label: labelToAvoid })
|
|
159
|
+
) {
|
|
160
|
+
const childLabels = this.initialNetLabelPlacements.filter((l) =>
|
|
161
|
+
originalNetIds.has(l.globalConnNetId),
|
|
162
|
+
)
|
|
163
|
+
this.decomposedChildLabels = childLabels
|
|
164
|
+
let actualOverlapLabel: NetLabelPlacement | null = null
|
|
165
|
+
|
|
166
|
+
for (const childLabel of childLabels) {
|
|
167
|
+
const overlapsWithChild = detectTraceLabelOverlap({
|
|
168
|
+
traces: [traceToFix],
|
|
169
|
+
netLabels: [childLabel],
|
|
170
|
+
})
|
|
171
|
+
if (overlapsWithChild.length > 0) {
|
|
172
|
+
actualOverlapLabel = childLabel
|
|
173
|
+
break
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (actualOverlapLabel) {
|
|
178
|
+
const labelId = actualOverlapLabel.globalConnNetId
|
|
179
|
+
const detourCount = this.detourCountByLabel[labelId] || 0
|
|
180
|
+
this.detourCountByLabel[labelId] = detourCount + 1
|
|
181
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
182
|
+
trace: traceToFix,
|
|
183
|
+
label: actualOverlapLabel,
|
|
184
|
+
problem: this.inputProblem,
|
|
185
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
186
|
+
detourCount,
|
|
187
|
+
})
|
|
188
|
+
} else {
|
|
189
|
+
const overlapId = `${traceToFix.mspPairId}-${labelToAvoid.globalConnNetId}`
|
|
190
|
+
this.recentlyFailed.add(overlapId)
|
|
191
|
+
}
|
|
192
|
+
return
|
|
118
193
|
}
|
|
194
|
+
|
|
195
|
+
const labelId = labelToAvoid.globalConnNetId
|
|
196
|
+
const detourCount = this.detourCountByLabel[labelId] || 0
|
|
197
|
+
this.detourCountByLabel[labelId] = detourCount + 1
|
|
198
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
199
|
+
trace: traceToFix,
|
|
200
|
+
label: labelToAvoid,
|
|
201
|
+
problem: this.inputProblem,
|
|
202
|
+
paddingBuffer: this.PADDING_BUFFER,
|
|
203
|
+
detourCount,
|
|
204
|
+
})
|
|
119
205
|
}
|
|
120
206
|
}
|
|
121
207
|
|
|
@@ -130,7 +216,7 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
|
130
216
|
if (this.activeSubSolver) {
|
|
131
217
|
return this.activeSubSolver.visualize()
|
|
132
218
|
}
|
|
133
|
-
|
|
219
|
+
|
|
134
220
|
const graphics = visualizeInputProblem(this.inputProblem)
|
|
135
221
|
if (!graphics.lines) graphics.lines = []
|
|
136
222
|
for (const trace of this.allTraces) {
|
|
@@ -139,6 +225,43 @@ export class OverlapAvoidanceStepSolver extends BaseSolver {
|
|
|
139
225
|
strokeColor: "purple",
|
|
140
226
|
})
|
|
141
227
|
}
|
|
228
|
+
|
|
229
|
+
if (this.currentlyProcessingOverlap) {
|
|
230
|
+
const { trace, label } = this.currentlyProcessingOverlap
|
|
231
|
+
|
|
232
|
+
// Highlight the colliding trace
|
|
233
|
+
graphics.lines!.push({
|
|
234
|
+
points: trace.tracePath,
|
|
235
|
+
strokeColor: "red",
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
if (this.decomposedChildLabels) {
|
|
239
|
+
visualizeDecomposition({
|
|
240
|
+
decomposedChildLabels: this.decomposedChildLabels,
|
|
241
|
+
collidingTrace: trace,
|
|
242
|
+
mergedLabel: label,
|
|
243
|
+
graphics,
|
|
244
|
+
})
|
|
245
|
+
} else {
|
|
246
|
+
// Standard case: highlight the entire label
|
|
247
|
+
if (!graphics.rects) graphics.rects = []
|
|
248
|
+
graphics.rects.push({
|
|
249
|
+
center: label.center,
|
|
250
|
+
width: label.width,
|
|
251
|
+
height: label.height,
|
|
252
|
+
fill: "yellow",
|
|
253
|
+
})
|
|
254
|
+
if (!graphics.texts) graphics.texts = []
|
|
255
|
+
graphics.texts.push({
|
|
256
|
+
x: label.center.x,
|
|
257
|
+
y: label.center.y + label.height / 2 + 0.5,
|
|
258
|
+
text: `COLLISION: Trace ${trace.mspPairId} vs Label ${label.globalConnNetId}`,
|
|
259
|
+
fontSize: 0.3,
|
|
260
|
+
color: "red",
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
142
265
|
return graphics
|
|
143
266
|
}
|
|
144
267
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Point } from "graphics-debug"
|
|
2
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
3
|
+
import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Checks if a point is inside the bounding box of a net label.
|
|
7
|
+
*/
|
|
8
|
+
export const isPointInsideLabel = ({
|
|
9
|
+
point,
|
|
10
|
+
label,
|
|
11
|
+
}: {
|
|
12
|
+
point: Point
|
|
13
|
+
label: NetLabelPlacement
|
|
14
|
+
}): boolean => {
|
|
15
|
+
const bounds = getRectBounds(label.center, label.width, label.height)
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
point.x >= bounds.minX &&
|
|
19
|
+
point.x <= bounds.maxX &&
|
|
20
|
+
point.y >= bounds.minY &&
|
|
21
|
+
point.y <= bounds.maxY
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
2
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
3
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
4
|
+
|
|
5
|
+
interface VisualizeDecompositionParams {
|
|
6
|
+
decomposedChildLabels: NetLabelPlacement[]
|
|
7
|
+
collidingTrace: SolvedTracePath
|
|
8
|
+
mergedLabel: NetLabelPlacement
|
|
9
|
+
graphics: GraphicsObject
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Visualizes the decomposition of a merged label during overlap avoidance.
|
|
14
|
+
* It draws individual child labels with distinct colors based on their relation
|
|
15
|
+
* to the colliding trace and adds a textual report to the graphics.
|
|
16
|
+
*
|
|
17
|
+
* @param {VisualizeDecompositionParams} params - The parameters for visualization.
|
|
18
|
+
* @param {NetLabelPlacement[]} params.decomposedChildLabels - The individual labels that make up the merged group.
|
|
19
|
+
* @param {SolvedTracePath} params.collidingTrace - The trace that is currently colliding with the merged label.
|
|
20
|
+
* @param {NetLabelPlacement} params.mergedLabel - The original merged label that is being decomposed.
|
|
21
|
+
* @param {GraphicsObject} params.graphics - The graphics object to which the visualization elements will be added.
|
|
22
|
+
* @returns {GraphicsObject} The modified graphics object with decomposition visualization elements.
|
|
23
|
+
*/
|
|
24
|
+
export const visualizeDecomposition = (
|
|
25
|
+
params: VisualizeDecompositionParams,
|
|
26
|
+
): GraphicsObject => {
|
|
27
|
+
const { decomposedChildLabels, collidingTrace, mergedLabel, graphics } =
|
|
28
|
+
params
|
|
29
|
+
|
|
30
|
+
if (!graphics.rects) graphics.rects = []
|
|
31
|
+
if (!graphics.texts) graphics.texts = []
|
|
32
|
+
|
|
33
|
+
for (const childLabel of decomposedChildLabels) {
|
|
34
|
+
const isOwnLabel =
|
|
35
|
+
childLabel.globalConnNetId === collidingTrace.globalConnNetId
|
|
36
|
+
|
|
37
|
+
graphics.rects.push({
|
|
38
|
+
center: childLabel.center,
|
|
39
|
+
width: childLabel.width,
|
|
40
|
+
height: childLabel.height,
|
|
41
|
+
fill: isOwnLabel ? "green" : "red", // Green for own label, red for others
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
graphics.texts.push({
|
|
46
|
+
x: mergedLabel.center.x,
|
|
47
|
+
y: mergedLabel.center.y + mergedLabel.height / 2 + 0.5,
|
|
48
|
+
text: `DECOMPOSITION: Trace ${collidingTrace.mspPairId} vs Merged Label ${mergedLabel.globalConnNetId}`,
|
|
49
|
+
fontSize: 0.3,
|
|
50
|
+
color: "blue",
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
return graphics
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -543,7 +543,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r
|
|
|
543
543
|
<circle data-type="point" data-label="" data-x="3.5999999999999996" data-y="0.5000000000000013" cx="413.0516274346982" cy="86.52581371734914" r="3" fill="hsl(80, 100%, 50%, 0.9)" />
|
|
544
544
|
</g>
|
|
545
545
|
<g>
|
|
546
|
-
<circle data-type="point" data-label="" data-x="-6.
|
|
546
|
+
<circle data-type="point" data-label="" data-x="-6.112500000000001" data-y="-12" cx="239.25087138479304" cy="310.2076104353738" r="3" fill="hsl(80, 100%, 50%, 0.9)" />
|
|
547
547
|
</g>
|
|
548
548
|
<g>
|
|
549
549
|
<circle data-type="point" data-label="" data-x="-4" data-y="-14.225" cx="277.0530950301392" cy="350.0229702511822" r="3" fill="hsl(80, 100%, 50%, 0.9)" />
|
|
@@ -849,7 +849,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r
|
|
|
849
849
|
<polyline data-points="-4.449999999999999,-10 -4,-10 -4,-10.45" data-type="line" data-label="" points="269.00055034829035,274.4185229604899 277.0530950301392,274.4185229604899 277.0530950301392,282.47106764233877" fill="none" stroke="purple" stroke-width="1" />
|
|
850
850
|
</g>
|
|
851
851
|
<g>
|
|
852
|
-
<polyline data-points="-5.550000000000001,-10 -5.750000000000001,-10 -5.750000000000001,-
|
|
852
|
+
<polyline data-points="-5.550000000000001,-10 -5.750000000000001,-10 -5.750000000000001,-9 -8.200000000000001,-9 -8.200000000000001,-5 -8.4,-5" data-type="line" data-label="" points="249.31655223710416,274.4185229604899 245.73764348961578,274.4185229604899 245.73764348961578,256.52397922304795 201.89601133288292,256.52397922304795 201.89601133288292,184.94580427328003 198.31710258539454,184.94580427328003" fill="none" stroke="purple" stroke-width="1" />
|
|
853
853
|
</g>
|
|
854
854
|
<g>
|
|
855
855
|
<polyline data-points="-4,-11.55 -4,-12 -3,-12 -3,-12.45" data-type="line" data-label="" points="277.0530950301392,302.1550657535249 277.0530950301392,310.2076104353738 294.9476387675812,310.2076104353738 294.9476387675812,318.2601551172227" fill="none" stroke="purple" stroke-width="1" />
|
|
@@ -858,7 +858,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r
|
|
|
858
858
|
<polyline data-points="-4.449999999999999,-12 -4,-12 -4,-11.55" data-type="line" data-label="" points="269.00055034829035,310.2076104353738 277.0530950301392,310.2076104353738 277.0530950301392,302.1550657535249" fill="none" stroke="purple" stroke-width="1" />
|
|
859
859
|
</g>
|
|
860
860
|
<g>
|
|
861
|
-
<polyline data-points="-5.550000000000001,-12 -6.
|
|
861
|
+
<polyline data-points="-5.550000000000001,-12 -6.675000000000001,-12 -6.675000000000001,-9.2 -8.4,-9.2" data-type="line" data-label="" points="249.31655223710416,310.2076104353738 229.18519053248195,310.2076104353738 229.18519053248195,260.1028879705363 198.31710258539454,260.1028879705363" fill="none" stroke="purple" stroke-width="1" />
|
|
862
862
|
</g>
|
|
863
863
|
<g>
|
|
864
864
|
<polyline data-points="-4,-14.45 -4,-14 -3,-14 -3,-13.55" data-type="line" data-label="" points="277.0530950301392,354.0492425921067 277.0530950301392,345.9966979102578 294.9476387675812,345.9966979102578 294.9476387675812,337.94415322840894" fill="none" stroke="purple" stroke-width="1" />
|
|
@@ -876,7 +876,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r
|
|
|
876
876
|
<polyline data-points="-3,-16.45 -3,-16 -4,-16 -4,-15.55" data-type="line" data-label="" points="294.9476387675812,389.8383300669906 294.9476387675812,381.7857853851417 277.0530950301392,381.7857853851417 277.0530950301392,373.73324070329284" fill="none" stroke="purple" stroke-width="1" />
|
|
877
877
|
</g>
|
|
878
878
|
<g>
|
|
879
|
-
<polyline data-points="-5.550000000000001,-16 -6.875000000000001,-16 -6.875000000000001,-
|
|
879
|
+
<polyline data-points="-5.550000000000001,-16 -6.875000000000001,-16 -6.875000000000001,-10 -8.4,-10" data-type="line" data-label="" points="249.31655223710416,381.7857853851417 225.60628178499354,381.7857853851417 225.60628178499354,274.4185229604899 198.31710258539454,274.4185229604899" fill="none" stroke="purple" stroke-width="1" />
|
|
880
880
|
</g>
|
|
881
881
|
<g>
|
|
882
882
|
<polyline data-points="-4,-18.45 -4,-18 -3,-18 -3,-17.55" data-type="line" data-label="" points="277.0530950301392,425.6274175418746 277.0530950301392,417.5748728600257 294.9476387675812,417.5748728600257 294.9476387675812,409.52232817817685" fill="none" stroke="purple" stroke-width="1" />
|
|
@@ -1056,7 +1056,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r
|
|
|
1056
1056
|
<rect data-type="rect" data-label="" data-x="3.3739999999999997" data-y="0.7000000000000015" x="404.9811882091119" y="81.15745059611653" width="8.052544681848872" height="3.5789087474883985" fill="hsl(80, 100%, 50%, 0.35)" stroke="black" stroke-width="0.05588295598214286" />
|
|
1057
1057
|
</g>
|
|
1058
1058
|
<g>
|
|
1059
|
-
<rect data-type="rect" data-label="" data-x="-5.750000000000001" data-y="-
|
|
1059
|
+
<rect data-type="rect" data-label="" data-x="-5.750000000000001" data-y="-10.225" x="243.94818911587157" y="274.4185229604899" width="3.5789087474883843" height="8.052544681848872" fill="hsl(80, 100%, 50%, 0.35)" stroke="black" stroke-width="0.05588295598214286" />
|
|
1060
1060
|
</g>
|
|
1061
1061
|
<g>
|
|
1062
1062
|
<rect data-type="rect" data-label="" data-x="-3.775" data-y="-11.775" x="277.0530950301392" y="304.3918837207052" width="8.052544681848929" height="3.5789087474884127" fill="hsl(80, 100%, 50%, 0.35)" stroke="black" stroke-width="0.05588295598214286" />
|
|
@@ -1065,7 +1065,7 @@ x+" data-x="-8.4" data-y="-17" cx="198.31710258539454" cy="399.68032912258366" r
|
|
|
1065
1065
|
<rect data-type="rect" data-label="" data-x="3.3739999999999997" data-y="0.5000000000000013" x="404.9811882091119" y="84.73635934360493" width="8.052544681848872" height="3.5789087474883985" fill="hsl(80, 100%, 50%, 0.35)" stroke="black" stroke-width="0.05588295598214286" />
|
|
1066
1066
|
</g>
|
|
1067
1067
|
<g>
|
|
1068
|
-
<rect data-type="rect" data-label="" data-x="-6.
|
|
1068
|
+
<rect data-type="rect" data-label="" data-x="-6.112500000000001" data-y="-11.775" x="237.46141701104887" y="302.1550657535249" width="3.578908747488356" height="8.052544681848872" fill="hsl(80, 100%, 50%, 0.35)" stroke="black" stroke-width="0.05588295598214286" />
|
|
1069
1069
|
</g>
|
|
1070
1070
|
<g>
|
|
1071
1071
|
<rect data-type="rect" data-label="" data-x="-3.775" data-y="-14.225" x="277.0530950301392" y="348.23351587743804" width="8.052544681848929" height="3.578908747488356" fill="hsl(80, 100%, 50%, 0.35)" stroke="black" stroke-width="0.05588295598214286" />
|
|
@@ -160,28 +160,28 @@ x-" data-x="-0.42" data-y="-0.1" cx="279.0224461302513" cy="382.68590086027035"
|
|
|
160
160
|
<rect data-type="rect" data-label="schematic_component_6" data-x="0" data-y="0" x="277.802876074604" y="349.51359534666426" width="43.416693981043295" height="56.58805058203393" fill="hsl(24, 100%, 50%, 0.8)" stroke="black" stroke-width="0.020499027410714285" />
|
|
161
161
|
</g>
|
|
162
162
|
<g>
|
|
163
|
-
<rect data-type="rect" data-label="
|
|
163
|
+
<rect data-type="rect" data-label="connectivity_net2" data-x="6" data-y="0.776" x="587.3297561978845" y="328.976035609564" width="9.756560445178138" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
|
|
164
164
|
</g>
|
|
165
165
|
<g>
|
|
166
|
-
<rect data-type="rect" data-label="
|
|
166
|
+
<rect data-type="rect" data-label="connectivity_net2" data-x="3.7199999999999998" data-y="3.225" x="476.10496712285226" y="209.50695295835615" width="9.756560445178252" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
|
|
167
167
|
</g>
|
|
168
168
|
<g>
|
|
169
|
-
<rect data-type="rect" data-label="
|
|
169
|
+
<rect data-type="rect" data-label="connectivity_net1" data-x="0.3" data-y="0.8059999999999999" x="309.2677835103039" y="327.51255154278726" width="9.756560445178309" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
|
|
170
170
|
</g>
|
|
171
171
|
<g>
|
|
172
|
-
<rect data-type="rect" data-label="
|
|
172
|
+
<rect data-type="rect" data-label="connectivity_net1" data-x="0.78" data-y="3.1950000000000003" x="332.68352857873174" y="210.97043702513287" width="9.756560445178252" height="21.952261001651124" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
|
|
173
173
|
</g>
|
|
174
174
|
<g>
|
|
175
|
-
<rect data-type="rect" data-label="
|
|
175
|
+
<rect data-type="rect" data-label="connectivity_net3" data-x="6" data-y="-0.7010000000000001" x="587.3297561978845" y="404.68694466414735" width="9.756560445178138" height="14.634840667767378" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
|
|
176
176
|
</g>
|
|
177
177
|
<g>
|
|
178
|
-
<rect data-type="rect" data-label="
|
|
178
|
+
<rect data-type="rect" data-label="connectivity_net3" data-x="3" data-y="-0.93" x="440.9813495202105" y="415.85820637387644" width="9.756560445178309" height="14.634840667767435" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
|
|
179
179
|
</g>
|
|
180
180
|
<g>
|
|
181
|
-
<rect data-type="rect" data-label="
|
|
181
|
+
<rect data-type="rect" data-label="connectivity_net0" data-x="-4.78" data-y="2.7600000000000002" x="55.35329792453959" y="238.288806271632" width="21.95226100165104" height="9.75656044517828" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
|
|
182
182
|
</g>
|
|
183
183
|
<g>
|
|
184
|
-
<rect data-type="rect" data-label="
|
|
184
|
+
<rect data-type="rect" data-label="connectivity_net4" data-x="-1.08373445" data-y="0.12500000000000014" x="241.7653395028014" y="360.73363985861926" width="9.75656044517828" height="21.952261001651095" fill="hsl(40, 100%, 50%, 0.3)" stroke="hsl(40, 100%, 50%, 1)" stroke-width="0.020499027410714285" />
|
|
185
185
|
</g>
|
|
186
186
|
<g id="crosshair" style="display: none">
|
|
187
187
|
<line id="crosshair-h" y1="0" y2="640" stroke="#666" stroke-width="0.5" />
|