@tscircuit/schematic-trace-solver 0.0.54 → 0.0.56
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 +31 -2
- package/dist/index.js +268 -2
- package/lib/solvers/LongDistancePairSolver/LongDistancePairSolver.ts +10 -1
- package/lib/solvers/MspConnectionPairSolver/MspConnectionPairSolver.ts +14 -0
- package/lib/solvers/NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver.ts +294 -0
- package/lib/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver.ts +15 -0
- package/lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem.ts +7 -0
- package/lib/types/InputProblem.ts +2 -0
- package/lib/utils/arePinsInDifferentSchematicSections.ts +39 -0
- package/package.json +1 -1
- package/site/examples/example32.page.tsx +4 -0
- package/site/examples/example33.page.tsx +4 -0
- package/tests/assets/example32.json +148 -0
- package/tests/assets/example33.json +132 -0
- package/tests/examples/__snapshots__/example32.snap.svg +391 -0
- package/tests/examples/__snapshots__/example33.snap.svg +181 -0
- package/tests/examples/example32.test.ts +12 -0
- package/tests/examples/example33.test.ts +12 -0
- package/tests/solvers/MspConnectionPairSolver/MspConnectionPairSolver_schematicSections.test.ts +70 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
2
|
+
import { BaseSolver } from "lib/solvers/BaseSolver/BaseSolver"
|
|
3
|
+
import type { NetLabelPlacement } from "lib/solvers/NetLabelPlacementSolver/NetLabelPlacementSolver"
|
|
4
|
+
import type { SolvedTracePath } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceLinesSolver"
|
|
5
|
+
import { visualizeInputProblem } from "lib/solvers/SchematicTracePipelineSolver/visualizeInputProblem"
|
|
6
|
+
import { SingleOverlapSolver } from "lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/SingleOverlapSolver/SingleOverlapSolver"
|
|
7
|
+
import { getRectBounds } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/geometry"
|
|
8
|
+
import { segmentIntersectsRect } from "lib/solvers/NetLabelPlacementSolver/SingleNetLabelPlacementSolver/collisions"
|
|
9
|
+
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
10
|
+
import type { InputProblem } from "lib/types/InputProblem"
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Merges all labels that touch/overlap with each other (transitively) around
|
|
14
|
+
* a seed label into a single bounding-box label. This prevents the situation
|
|
15
|
+
* where fixing one label collision re-routes the trace into an adjacent label.
|
|
16
|
+
*/
|
|
17
|
+
function buildMergedObstacleLabel(
|
|
18
|
+
seedLabel: NetLabelPlacement,
|
|
19
|
+
allLabels: NetLabelPlacement[],
|
|
20
|
+
): NetLabelPlacement {
|
|
21
|
+
const TOUCH_MARGIN = 0.01
|
|
22
|
+
|
|
23
|
+
const getBounds = (l: NetLabelPlacement) =>
|
|
24
|
+
getRectBounds(l.center, l.width, l.height)
|
|
25
|
+
|
|
26
|
+
const touches = (a: NetLabelPlacement, b: NetLabelPlacement) => {
|
|
27
|
+
const ba = getBounds(a)
|
|
28
|
+
const bb = getBounds(b)
|
|
29
|
+
return (
|
|
30
|
+
ba.minX <= bb.maxX + TOUCH_MARGIN &&
|
|
31
|
+
ba.maxX >= bb.minX - TOUCH_MARGIN &&
|
|
32
|
+
ba.minY <= bb.maxY + TOUCH_MARGIN &&
|
|
33
|
+
ba.maxY >= bb.minY - TOUCH_MARGIN
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const group = new Set<NetLabelPlacement>([seedLabel])
|
|
38
|
+
let changed = true
|
|
39
|
+
while (changed) {
|
|
40
|
+
changed = false
|
|
41
|
+
for (const l of allLabels) {
|
|
42
|
+
if (group.has(l)) continue
|
|
43
|
+
if ([...group].some((g) => touches(g, l))) {
|
|
44
|
+
group.add(l)
|
|
45
|
+
changed = true
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let minX = Infinity,
|
|
51
|
+
minY = Infinity,
|
|
52
|
+
maxX = -Infinity,
|
|
53
|
+
maxY = -Infinity
|
|
54
|
+
for (const l of group) {
|
|
55
|
+
const b = getBounds(l)
|
|
56
|
+
if (b.minX < minX) minX = b.minX
|
|
57
|
+
if (b.minY < minY) minY = b.minY
|
|
58
|
+
if (b.maxX > maxX) maxX = b.maxX
|
|
59
|
+
if (b.maxY > maxY) maxY = b.maxY
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const cx = (minX + maxX) / 2
|
|
63
|
+
const cy = (minY + maxY) / 2
|
|
64
|
+
const w = maxX - minX
|
|
65
|
+
const h = maxY - minY
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
...seedLabel,
|
|
69
|
+
center: { x: cx, y: cy },
|
|
70
|
+
width: w,
|
|
71
|
+
height: h,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface TraceInnerLabelOverlap {
|
|
76
|
+
trace: SolvedTracePath
|
|
77
|
+
label: NetLabelPlacement
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Detects overlaps where a trace's INTERIOR (non-endpoint) segments cross a label.
|
|
82
|
+
* Traces are allowed to boundary a label at their endpoints (pin locations),
|
|
83
|
+
* just not allowed to cross through it in the middle.
|
|
84
|
+
*/
|
|
85
|
+
function detectInnerTraceLabelOverlaps(
|
|
86
|
+
traces: SolvedTracePath[],
|
|
87
|
+
labels: NetLabelPlacement[],
|
|
88
|
+
): TraceInnerLabelOverlap[] {
|
|
89
|
+
const overlaps: TraceInnerLabelOverlap[] = []
|
|
90
|
+
for (const trace of traces) {
|
|
91
|
+
for (const label of labels) {
|
|
92
|
+
if (trace.globalConnNetId === label.globalConnNetId) continue
|
|
93
|
+
const bounds = getRectBounds(label.center, label.width, label.height)
|
|
94
|
+
const path = trace.tracePath
|
|
95
|
+
// Skip first and last segments — trace endpoints (pins) may legitimately
|
|
96
|
+
// sit inside or adjacent to labels of neighbouring nets.
|
|
97
|
+
const innerStart = 1
|
|
98
|
+
const innerEnd = path.length - 2
|
|
99
|
+
for (let i = innerStart; i < innerEnd; i++) {
|
|
100
|
+
if (segmentIntersectsRect(path[i]!, path[i + 1]!, bounds)) {
|
|
101
|
+
overlaps.push({ trace, label })
|
|
102
|
+
break
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return overlaps
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface NetLabelTraceCollisionSolverParams {
|
|
111
|
+
inputProblem: InputProblem
|
|
112
|
+
traces: SolvedTracePath[]
|
|
113
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const PADDING_BUFFER = 0.1
|
|
117
|
+
const MAX_DETOUR_ATTEMPTS = 3
|
|
118
|
+
|
|
119
|
+
export class NetLabelTraceCollisionSolver extends BaseSolver {
|
|
120
|
+
inputProblem: InputProblem
|
|
121
|
+
traces: SolvedTracePath[]
|
|
122
|
+
netLabelPlacements: NetLabelPlacement[]
|
|
123
|
+
|
|
124
|
+
outputTraces: SolvedTracePath[]
|
|
125
|
+
outputNetLabelPlacements: NetLabelPlacement[]
|
|
126
|
+
|
|
127
|
+
override activeSubSolver: SingleOverlapSolver | null = null
|
|
128
|
+
private recentlyFailed = new Set<string>()
|
|
129
|
+
private detourCounts = new Map<string, number>()
|
|
130
|
+
|
|
131
|
+
private currentOverlap: {
|
|
132
|
+
trace: SolvedTracePath
|
|
133
|
+
label: NetLabelPlacement
|
|
134
|
+
} | null = null
|
|
135
|
+
|
|
136
|
+
constructor(params: NetLabelTraceCollisionSolverParams) {
|
|
137
|
+
super()
|
|
138
|
+
this.inputProblem = params.inputProblem
|
|
139
|
+
this.traces = params.traces
|
|
140
|
+
this.netLabelPlacements = params.netLabelPlacements
|
|
141
|
+
this.outputTraces = [...params.traces]
|
|
142
|
+
this.outputNetLabelPlacements = [...params.netLabelPlacements]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
override getConstructorParams(): ConstructorParameters<
|
|
146
|
+
typeof NetLabelTraceCollisionSolver
|
|
147
|
+
>[0] {
|
|
148
|
+
return {
|
|
149
|
+
inputProblem: this.inputProblem,
|
|
150
|
+
traces: this.traces,
|
|
151
|
+
netLabelPlacements: this.netLabelPlacements,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
override _step() {
|
|
156
|
+
if (this.activeSubSolver) {
|
|
157
|
+
this.activeSubSolver.step()
|
|
158
|
+
|
|
159
|
+
if (this.activeSubSolver.solved) {
|
|
160
|
+
const solvedPath = this.activeSubSolver.solvedTracePath
|
|
161
|
+
if (solvedPath) {
|
|
162
|
+
const idx = this.outputTraces.findIndex(
|
|
163
|
+
(t) => t.mspPairId === this.activeSubSolver!.initialTrace.mspPairId,
|
|
164
|
+
)
|
|
165
|
+
if (idx !== -1) {
|
|
166
|
+
this.outputTraces[idx] = {
|
|
167
|
+
...this.outputTraces[idx],
|
|
168
|
+
tracePath: solvedPath,
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
this.activeSubSolver = null
|
|
173
|
+
this.currentOverlap = null
|
|
174
|
+
} else if (this.activeSubSolver.failed) {
|
|
175
|
+
const key = this.getOverlapKey(
|
|
176
|
+
this.activeSubSolver.initialTrace,
|
|
177
|
+
this.activeSubSolver.label,
|
|
178
|
+
)
|
|
179
|
+
this.recentlyFailed.add(key)
|
|
180
|
+
this.activeSubSolver = null
|
|
181
|
+
this.currentOverlap = null
|
|
182
|
+
}
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const overlaps = detectInnerTraceLabelOverlaps(
|
|
187
|
+
this.outputTraces,
|
|
188
|
+
this.outputNetLabelPlacements,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
const actionable = overlaps.filter((o) => {
|
|
192
|
+
const key = this.getOverlapKey(o.trace, o.label)
|
|
193
|
+
return !this.recentlyFailed.has(key)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
if (actionable.length === 0) {
|
|
197
|
+
this.solved = true
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const next = actionable[0]
|
|
202
|
+
|
|
203
|
+
const traceToFix = this.outputTraces.find(
|
|
204
|
+
(t) => t.mspPairId === next.trace.mspPairId,
|
|
205
|
+
)!
|
|
206
|
+
|
|
207
|
+
this.currentOverlap = next
|
|
208
|
+
|
|
209
|
+
// Merge all touching/overlapping labels into one obstacle so that fixing
|
|
210
|
+
// one collision doesn't immediately create another with an adjacent label.
|
|
211
|
+
const mergedLabel = buildMergedObstacleLabel(
|
|
212
|
+
next.label,
|
|
213
|
+
this.outputNetLabelPlacements.filter(
|
|
214
|
+
(l) => l.globalConnNetId !== traceToFix.globalConnNetId,
|
|
215
|
+
),
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
const mergeKey = `${next.trace.mspPairId}::merged::${mergedLabel.center.x},${mergedLabel.center.y},${mergedLabel.width},${mergedLabel.height}`
|
|
219
|
+
const detourCount = this.detourCounts.get(mergeKey) ?? 0
|
|
220
|
+
if (detourCount >= MAX_DETOUR_ATTEMPTS) {
|
|
221
|
+
this.recentlyFailed.add(this.getOverlapKey(next.trace, next.label))
|
|
222
|
+
this.currentOverlap = null
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
this.detourCounts.set(mergeKey, detourCount + 1)
|
|
226
|
+
|
|
227
|
+
this.activeSubSolver = new SingleOverlapSolver({
|
|
228
|
+
trace: traceToFix,
|
|
229
|
+
label: mergedLabel,
|
|
230
|
+
problem: this.inputProblem,
|
|
231
|
+
paddingBuffer: PADDING_BUFFER,
|
|
232
|
+
detourCount,
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
getOutput() {
|
|
237
|
+
return {
|
|
238
|
+
traces: this.outputTraces,
|
|
239
|
+
netLabelPlacements: this.outputNetLabelPlacements,
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private getOverlapKey(trace: SolvedTracePath, label: NetLabelPlacement) {
|
|
244
|
+
return `${trace.mspPairId}::${label.globalConnNetId}`
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
override visualize(): GraphicsObject {
|
|
248
|
+
if (this.activeSubSolver) return this.activeSubSolver.visualize()
|
|
249
|
+
|
|
250
|
+
const graphics = visualizeInputProblem(this.inputProblem)
|
|
251
|
+
if (!graphics.lines) graphics.lines = []
|
|
252
|
+
if (!graphics.rects) graphics.rects = []
|
|
253
|
+
if (!graphics.points) graphics.points = []
|
|
254
|
+
|
|
255
|
+
for (const trace of this.outputTraces) {
|
|
256
|
+
graphics.lines.push({
|
|
257
|
+
points: trace.tracePath,
|
|
258
|
+
strokeColor: "purple",
|
|
259
|
+
} as any)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
for (const label of this.outputNetLabelPlacements) {
|
|
263
|
+
graphics.rects.push({
|
|
264
|
+
center: label.center,
|
|
265
|
+
width: label.width,
|
|
266
|
+
height: label.height,
|
|
267
|
+
fill: getColorFromString(label.globalConnNetId, 0.35),
|
|
268
|
+
strokeColor: getColorFromString(label.globalConnNetId, 0.9),
|
|
269
|
+
label: `netId: ${label.netId}\nglobalConnNetId: ${label.globalConnNetId}`,
|
|
270
|
+
} as any)
|
|
271
|
+
graphics.points.push({
|
|
272
|
+
x: label.anchorPoint.x,
|
|
273
|
+
y: label.anchorPoint.y,
|
|
274
|
+
color: getColorFromString(label.globalConnNetId, 0.9),
|
|
275
|
+
} as any)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (this.currentOverlap) {
|
|
279
|
+
graphics.lines.push({
|
|
280
|
+
points: this.currentOverlap.trace.tracePath,
|
|
281
|
+
strokeColor: "red",
|
|
282
|
+
} as any)
|
|
283
|
+
graphics.rects.push({
|
|
284
|
+
center: this.currentOverlap.label.center,
|
|
285
|
+
width: this.currentOverlap.label.width,
|
|
286
|
+
height: this.currentOverlap.label.height,
|
|
287
|
+
fill: "rgba(255,0,0,0.3)",
|
|
288
|
+
strokeColor: "red",
|
|
289
|
+
} as any)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return graphics
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -25,6 +25,7 @@ import { Example28Solver } from "../Example28Solver/Example28Solver"
|
|
|
25
25
|
import { AvailableNetOrientationSolver } from "../AvailableNetOrientationSolver/AvailableNetOrientationSolver"
|
|
26
26
|
import { VccNetLabelCornerPlacementSolver } from "../VccNetLabelCornerPlacementSolver/VccNetLabelCornerPlacementSolver"
|
|
27
27
|
import { TraceAnchoredNetLabelOverlapSolver } from "../TraceAnchoredNetLabelOverlapSolver/TraceAnchoredNetLabelOverlapSolver"
|
|
28
|
+
import { NetLabelTraceCollisionSolver } from "../NetLabelTraceCollisionSolver/NetLabelTraceCollisionSolver"
|
|
28
29
|
|
|
29
30
|
type PipelineStep<T extends new (...args: any[]) => BaseSolver> = {
|
|
30
31
|
solverName: string
|
|
@@ -78,6 +79,7 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
78
79
|
availableNetOrientationSolver?: AvailableNetOrientationSolver
|
|
79
80
|
vccNetLabelCornerPlacementSolver?: VccNetLabelCornerPlacementSolver
|
|
80
81
|
traceAnchoredNetLabelOverlapSolver?: TraceAnchoredNetLabelOverlapSolver
|
|
82
|
+
netLabelTraceCollisionSolver?: NetLabelTraceCollisionSolver
|
|
81
83
|
|
|
82
84
|
startTimeOfPhase: Record<string, number>
|
|
83
85
|
endTimeOfPhase: Record<string, number>
|
|
@@ -285,6 +287,19 @@ export class SchematicTracePipelineSolver extends BaseSolver {
|
|
|
285
287
|
},
|
|
286
288
|
],
|
|
287
289
|
),
|
|
290
|
+
definePipelineStep(
|
|
291
|
+
"netLabelTraceCollisionSolver",
|
|
292
|
+
NetLabelTraceCollisionSolver,
|
|
293
|
+
(instance) => [
|
|
294
|
+
{
|
|
295
|
+
inputProblem: instance.inputProblem,
|
|
296
|
+
traces: instance.availableNetOrientationSolver!.traces,
|
|
297
|
+
netLabelPlacements:
|
|
298
|
+
instance.traceAnchoredNetLabelOverlapSolver!
|
|
299
|
+
.outputNetLabelPlacements,
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
),
|
|
288
303
|
]
|
|
289
304
|
|
|
290
305
|
constructor(inputProblem: InputProblem) {
|
|
@@ -2,6 +2,7 @@ import type { GraphicsObject } from "graphics-debug"
|
|
|
2
2
|
import type { PinId, InputPin, InputProblem } from "lib/types/InputProblem"
|
|
3
3
|
import { getColorFromString } from "lib/utils/getColorFromString"
|
|
4
4
|
import { getPinDirection } from "../SchematicTraceLinesSolver/SchematicTraceSingleLineSolver/getPinDirection"
|
|
5
|
+
import { arePinsInDifferentSchematicSections } from "../../utils/arePinsInDifferentSchematicSections"
|
|
5
6
|
|
|
6
7
|
export const visualizeInputProblem = (
|
|
7
8
|
inputProblem: InputProblem,
|
|
@@ -50,6 +51,9 @@ export const visualizeInputProblem = (
|
|
|
50
51
|
const [pinId1, pinId2] = directConn.pinIds
|
|
51
52
|
const pin1 = pinIdMap.get(pinId1)!
|
|
52
53
|
const pin2 = pinIdMap.get(pinId2)!
|
|
54
|
+
if (arePinsInDifferentSchematicSections(inputProblem, pin1, pin2)) {
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
53
57
|
graphics.lines.push({
|
|
54
58
|
points: [
|
|
55
59
|
{
|
|
@@ -74,6 +78,9 @@ export const visualizeInputProblem = (
|
|
|
74
78
|
for (let j = i + 1; j < pins.length; j++) {
|
|
75
79
|
const pin1 = pins[i]!
|
|
76
80
|
const pin2 = pins[j]!
|
|
81
|
+
if (arePinsInDifferentSchematicSections(inputProblem, pin1, pin2)) {
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
77
84
|
graphics.lines.push({
|
|
78
85
|
points: [
|
|
79
86
|
{ x: pin1.x, y: pin1.y },
|
|
@@ -4,6 +4,7 @@ import type { FacingDirection } from "lib/utils/dir"
|
|
|
4
4
|
export type ChipId = string
|
|
5
5
|
export type PinId = string
|
|
6
6
|
export type NetId = string
|
|
7
|
+
export type SectionId = string
|
|
7
8
|
|
|
8
9
|
export interface InputPin {
|
|
9
10
|
pinId: PinId
|
|
@@ -18,6 +19,7 @@ export interface InputChip {
|
|
|
18
19
|
width: number
|
|
19
20
|
height: number
|
|
20
21
|
pins: Array<InputPin>
|
|
22
|
+
sectionId?: SectionId
|
|
21
23
|
}
|
|
22
24
|
export interface InputDirectConnection {
|
|
23
25
|
pinIds: [PinId, PinId]
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { InputProblem, InputPin } from "lib/types/InputProblem"
|
|
2
|
+
|
|
3
|
+
const getSectionNameForPin = (
|
|
4
|
+
sectionByChipId: Map<string, string>,
|
|
5
|
+
sectionByPinId: Map<string, string>,
|
|
6
|
+
pin: InputPin & { chipId?: string },
|
|
7
|
+
) => {
|
|
8
|
+
if (pin.chipId) {
|
|
9
|
+
const chipSection = sectionByChipId.get(pin.chipId)
|
|
10
|
+
if (chipSection) return chipSection
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return sectionByPinId.get(pin.pinId)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const arePinsInDifferentSchematicSections = (
|
|
17
|
+
inputProblem: InputProblem,
|
|
18
|
+
p1: InputPin & { chipId?: string },
|
|
19
|
+
p2: InputPin & { chipId?: string },
|
|
20
|
+
) => {
|
|
21
|
+
const sectionByChipId = new Map<string, string>()
|
|
22
|
+
const sectionByPinId = new Map<string, string>()
|
|
23
|
+
|
|
24
|
+
for (const chip of inputProblem.chips) {
|
|
25
|
+
if (!chip.sectionId) continue
|
|
26
|
+
|
|
27
|
+
sectionByChipId.set(chip.chipId, chip.sectionId)
|
|
28
|
+
for (const pin of chip.pins) {
|
|
29
|
+
sectionByPinId.set(pin.pinId, chip.sectionId)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (sectionByChipId.size === 0) return false
|
|
34
|
+
|
|
35
|
+
const s1 = getSectionNameForPin(sectionByChipId, sectionByPinId, p1)
|
|
36
|
+
const s2 = getSectionNameForPin(sectionByChipId, sectionByPinId, p2)
|
|
37
|
+
|
|
38
|
+
return !!s1 && !!s2 && s1 !== s2
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
{
|
|
2
|
+
"chips": [
|
|
3
|
+
{
|
|
4
|
+
"chipId": "schematic_component_0",
|
|
5
|
+
"center": { "x": -4, "y": 3 },
|
|
6
|
+
"width": 0.95,
|
|
7
|
+
"height": 0.29,
|
|
8
|
+
"sectionId": "power",
|
|
9
|
+
"pins": [
|
|
10
|
+
{ "pinId": "B1.1", "x": -4.45, "y": 2.9699999999999998 },
|
|
11
|
+
{ "pinId": "B1.2", "x": -3.55, "y": 2.9699999999999998 }
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"chipId": "schematic_component_1",
|
|
16
|
+
"center": { "x": -2, "y": 3 },
|
|
17
|
+
"width": 0.76,
|
|
18
|
+
"height": 0.5,
|
|
19
|
+
"sectionId": "power",
|
|
20
|
+
"pins": [
|
|
21
|
+
{ "pinId": "SW1.1", "x": -2.38, "y": 3 },
|
|
22
|
+
{ "pinId": "SW1.2", "x": -1.62, "y": 3 }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"chipId": "schematic_component_2",
|
|
27
|
+
"center": { "x": -3, "y": 1.5 },
|
|
28
|
+
"width": 1.1,
|
|
29
|
+
"height": 0.84,
|
|
30
|
+
"sectionId": "power",
|
|
31
|
+
"pins": [
|
|
32
|
+
{ "pinId": "C3.1", "x": -3.55, "y": 1.5 },
|
|
33
|
+
{ "pinId": "C3.2", "x": -2.45, "y": 1.5 }
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"chipId": "schematic_component_3",
|
|
38
|
+
"center": { "x": 1.5, "y": 2.5 },
|
|
39
|
+
"width": 1.6,
|
|
40
|
+
"height": 1,
|
|
41
|
+
"sectionId": "timer",
|
|
42
|
+
"pins": [
|
|
43
|
+
{ "pinId": "U1.1", "x": 2.7, "y": 2.2 },
|
|
44
|
+
{ "pinId": "U1.2", "x": 0.2999999999999998, "y": 2.2 },
|
|
45
|
+
{ "pinId": "U1.3", "x": 2.7, "y": 2.6 },
|
|
46
|
+
{ "pinId": "U1.4", "x": 0.2999999999999998, "y": 2.8 },
|
|
47
|
+
{ "pinId": "U1.5", "x": 0.2999999999999998, "y": 2.6 },
|
|
48
|
+
{ "pinId": "U1.6", "x": 0.2999999999999998, "y": 2.4 },
|
|
49
|
+
{ "pinId": "U1.7", "x": 2.7, "y": 2.4 },
|
|
50
|
+
{ "pinId": "U1.8", "x": 2.7, "y": 2.8 }
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"chipId": "schematic_component_4",
|
|
55
|
+
"center": { "x": 4, "y": 1.5 },
|
|
56
|
+
"width": 1.1,
|
|
57
|
+
"height": 0.84,
|
|
58
|
+
"sectionId": "timer",
|
|
59
|
+
"pins": [
|
|
60
|
+
{ "pinId": "C2.1", "x": 3.45, "y": 1.5 },
|
|
61
|
+
{ "pinId": "C2.2", "x": 4.55, "y": 1.5 }
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"chipId": "schematic_component_5",
|
|
66
|
+
"center": { "x": -4, "y": -1.5 },
|
|
67
|
+
"width": 1.1,
|
|
68
|
+
"height": 0.388910699999999,
|
|
69
|
+
"sectionId": "timing-network",
|
|
70
|
+
"pins": [
|
|
71
|
+
{ "pinId": "R1.1", "x": -4.55, "y": -1.5 },
|
|
72
|
+
{ "pinId": "R1.2", "x": -3.4499999999999997, "y": -1.5 }
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"chipId": "schematic_component_6",
|
|
77
|
+
"center": { "x": -2, "y": -1.5 },
|
|
78
|
+
"width": 1.1,
|
|
79
|
+
"height": 0.388910699999999,
|
|
80
|
+
"sectionId": "timing-network",
|
|
81
|
+
"pins": [
|
|
82
|
+
{ "pinId": "R2.1", "x": -2.55, "y": -1.5 },
|
|
83
|
+
{ "pinId": "R2.2", "x": -1.4500000000000002, "y": -1.5 }
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"chipId": "schematic_component_7",
|
|
88
|
+
"center": { "x": -3, "y": -3 },
|
|
89
|
+
"width": 1.1,
|
|
90
|
+
"height": 0.84,
|
|
91
|
+
"sectionId": "timing-network",
|
|
92
|
+
"pins": [
|
|
93
|
+
{ "pinId": "C1.1", "x": -3.55, "y": -3 },
|
|
94
|
+
{ "pinId": "C1.2", "x": -2.45, "y": -3 }
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"chipId": "schematic_component_8",
|
|
99
|
+
"center": { "x": 2.5, "y": -1.5 },
|
|
100
|
+
"width": 1.1,
|
|
101
|
+
"height": 0.388910699999999,
|
|
102
|
+
"sectionId": "output",
|
|
103
|
+
"pins": [
|
|
104
|
+
{ "pinId": "R3.1", "x": 1.95, "y": -1.5 },
|
|
105
|
+
{ "pinId": "R3.2", "x": 3.05, "y": -1.5 }
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"chipId": "schematic_component_9",
|
|
110
|
+
"center": { "x": 4.5, "y": -1.5 },
|
|
111
|
+
"width": 1.13,
|
|
112
|
+
"height": 0.65,
|
|
113
|
+
"sectionId": "output",
|
|
114
|
+
"pins": [
|
|
115
|
+
{ "pinId": "D1.1", "x": 3.96, "y": -1.5 },
|
|
116
|
+
{ "pinId": "D1.2", "x": 5.04, "y": -1.5 }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
"directConnections": [
|
|
121
|
+
{ "pinIds": ["B1.1", "SW1.1"], "netId": ".B1 > .pin1 to .SW1 > .pin1" },
|
|
122
|
+
{ "pinIds": ["U1.5", "C2.1"], "netId": ".U1 > .CTRL to .C2 > .pin1" },
|
|
123
|
+
{ "pinIds": ["U1.6", "U1.2"], "netId": ".U1 > .THRES to .U1 > .TRIG" },
|
|
124
|
+
{ "pinIds": ["R1.2", "U1.7"], "netId": ".R1 > .pin2 to .U1 > .DISCH" },
|
|
125
|
+
{ "pinIds": ["U1.7", "R2.1"], "netId": ".U1 > .DISCH to .R2 > .pin1" },
|
|
126
|
+
{ "pinIds": ["R2.2", "U1.6"], "netId": ".R2 > .pin2 to .U1 > .THRES" },
|
|
127
|
+
{ "pinIds": ["U1.6", "C1.1"], "netId": ".U1 > .THRES to .C1 > .pin1" },
|
|
128
|
+
{ "pinIds": ["U1.3", "R3.1"], "netId": ".U1 > .OUT to .R3 > .pin1" },
|
|
129
|
+
{ "pinIds": ["R3.2", "D1.1"], "netId": ".R3 > .pin2 to .D1 > .pin1" }
|
|
130
|
+
],
|
|
131
|
+
"netConnections": [
|
|
132
|
+
{
|
|
133
|
+
"netId": "GND",
|
|
134
|
+
"pinIds": ["B1.2", "C3.2", "U1.1", "C2.2", "C1.2", "D1.2"],
|
|
135
|
+
"netLabelWidth": 0.3
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"netId": "VCC",
|
|
139
|
+
"pinIds": ["SW1.2", "C3.1", "U1.4", "U1.8", "R1.1"],
|
|
140
|
+
"netLabelWidth": 0.3
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
"availableNetLabelOrientations": {
|
|
144
|
+
"VCC": ["y+"],
|
|
145
|
+
"GND": ["y-"]
|
|
146
|
+
},
|
|
147
|
+
"maxMspPairDistance": 2.4
|
|
148
|
+
}
|