@tscircuit/copper-pour-solver 0.0.33 → 0.0.35
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/README.md +148 -20
- package/cosmos.config.json +2 -1
- package/dist/index.d.ts +15 -5
- package/dist/index.js +252 -8
- package/lib/circuit-json/ConvertCircuitJsonToInputProblemOptions.ts +19 -0
- package/lib/circuit-json/buildSubcircuitConnectivityLookup.ts +237 -0
- package/lib/circuit-json/convert-circuit-json-to-input-problem.ts +22 -22
- package/lib/circuit-json/getElementSubcircuitConnectivityKey.ts +8 -0
- package/lib/circuit-json/resolvePourConnectivityKey.ts +88 -0
- package/lib/index.ts +1 -0
- package/package.json +8 -3
- package/site/Welcome.page.tsx +214 -0
- package/tests/__snapshots__/tsx-subcircuit-connectivity01.snap.svg +1 -0
- package/tests/__snapshots__/tsx-subcircuit-connectivity02.snap.svg +1 -0
- package/tests/assets/subcircuit-connectivity-scope.json +126 -0
- package/tests/connectivity-key-api.test.ts +146 -0
- package/tests/pill-pad-copper-pour.test.ts +1 -1
- package/tests/repro01-business-via-card/__snapshots__/repro01-business-via-card.snap.svg +1 -1
- package/tests/tsx-subcircuit-connectivity.test.tsx +223 -0
- package/tests/utils/run-solver-and-render-to-svg.ts +5 -13
- package/vercel.json +5 -0
package/README.md
CHANGED
|
@@ -1,37 +1,165 @@
|
|
|
1
1
|
# @tscircuit/copper-pour-solver
|
|
2
2
|
|
|
3
|
-
Solves
|
|
3
|
+
Solves PCB copper pour regions from Circuit JSON or from a small geometry input
|
|
4
|
+
format, returning `pcb_copper_pour`-ready B-Rep shapes.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
import { CopperPourPipelineSolver } from "@tscircuit/copper-pour-solver"
|
|
6
|
+
## Install
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
```bash
|
|
9
|
+
bun add @tscircuit/copper-pour-solver
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
This package expects TypeScript 5 as a peer dependency.
|
|
13
|
+
|
|
14
|
+
## Basic Usage With Circuit JSON
|
|
15
|
+
|
|
16
|
+
Initialize the geometry runtime once before solving. Then convert Circuit JSON
|
|
17
|
+
into the solver input format, run the solver, and map the returned B-Rep shapes
|
|
18
|
+
back into `pcb_copper_pour` elements.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import {
|
|
22
|
+
CopperPourPipelineSolver,
|
|
23
|
+
convertCircuitJsonToInputProblem,
|
|
24
|
+
initializeManifoldGeometry,
|
|
25
|
+
} from "@tscircuit/copper-pour-solver"
|
|
26
|
+
|
|
27
|
+
await initializeManifoldGeometry()
|
|
28
|
+
|
|
29
|
+
const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
|
|
30
|
+
layer: "top",
|
|
31
|
+
source_net_name: "GND",
|
|
32
|
+
pad_margin: 0.4,
|
|
33
|
+
trace_margin: 0.2,
|
|
34
|
+
board_edge_margin: 0.1,
|
|
35
|
+
cutout_margin: 0.2,
|
|
11
36
|
})
|
|
12
37
|
|
|
13
|
-
solver
|
|
38
|
+
const solver = new CopperPourPipelineSolver(inputProblem)
|
|
39
|
+
const { brep_shapes } = solver.getOutput()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`convertCircuitJsonToInputProblem` reads board bounds or outline, SMT pads,
|
|
43
|
+
plated holes, mechanical holes, vias, traces, and cutouts for the selected layer.
|
|
44
|
+
Pads and traces connected to the selected source net are kept connected to the
|
|
45
|
+
pour; unrelated geometry is subtracted using the configured margins.
|
|
14
46
|
|
|
15
|
-
|
|
16
|
-
|
|
47
|
+
## Selecting The Pour Net
|
|
48
|
+
|
|
49
|
+
Prefer selecting by source net name or id:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
|
|
53
|
+
layer: "top",
|
|
54
|
+
source_net_name: "GND",
|
|
55
|
+
pad_margin: 0.4,
|
|
56
|
+
trace_margin: 0.2,
|
|
57
|
+
})
|
|
17
58
|
```
|
|
18
59
|
|
|
19
|
-
|
|
60
|
+
You can also pass the source net's stable `subcircuit_connectivity_map_key`
|
|
61
|
+
directly:
|
|
20
62
|
|
|
21
|
-
|
|
63
|
+
```ts
|
|
64
|
+
const gnd = circuitJson.find(
|
|
65
|
+
(element) => element.type === "source_net" && element.name === "GND",
|
|
66
|
+
)
|
|
22
67
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
68
|
+
const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
|
|
69
|
+
layer: "top",
|
|
70
|
+
subcircuit_id: gnd.subcircuit_id,
|
|
71
|
+
subcircuit_connectivity_map_key: gnd.subcircuit_connectivity_map_key,
|
|
72
|
+
pad_margin: 0.4,
|
|
73
|
+
trace_margin: 0.2,
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Pass `subcircuit_id` when selecting a net inside a subcircuit. The converter
|
|
78
|
+
considers that subcircuit and its child subcircuits, but it does not treat
|
|
79
|
+
matching child `subcircuit_connectivity_map_key` values as connected unless the
|
|
80
|
+
Circuit JSON connectivity actually connects them. Internally, the generated
|
|
81
|
+
`globalConnectivityMap` is kept separate from the scoped
|
|
82
|
+
`subcircuitConnectivityMap`; scoped solver connectivity keys are prefixed with
|
|
83
|
+
their subcircuit id.
|
|
84
|
+
|
|
85
|
+
Do not generate or pass ids from `circuit-json-to-connectivity-map`. The
|
|
86
|
+
converter handles PCB connectivity internally and normalizes it to stable
|
|
87
|
+
`subcircuit_connectivity_map_key` values.
|
|
88
|
+
|
|
89
|
+
## Manual Input
|
|
90
|
+
|
|
91
|
+
You can skip Circuit JSON conversion and provide the solver input directly.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import {
|
|
95
|
+
CopperPourPipelineSolver,
|
|
96
|
+
initializeManifoldGeometry,
|
|
97
|
+
type InputProblem,
|
|
98
|
+
} from "@tscircuit/copper-pour-solver"
|
|
99
|
+
|
|
100
|
+
await initializeManifoldGeometry()
|
|
101
|
+
|
|
102
|
+
const input: InputProblem = {
|
|
103
|
+
regionsForPour: [
|
|
104
|
+
{
|
|
105
|
+
shape: "rect",
|
|
106
|
+
layer: "top",
|
|
107
|
+
bounds: { minX: -10, minY: -5, maxX: 10, maxY: 5 },
|
|
108
|
+
connectivityKey: "net:GND",
|
|
109
|
+
padMargin: 0.4,
|
|
110
|
+
traceMargin: 0.2,
|
|
111
|
+
board_edge_margin: 0.1,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
pads: [
|
|
115
|
+
{
|
|
116
|
+
shape: "circle",
|
|
117
|
+
padId: "via_1",
|
|
118
|
+
layer: "top",
|
|
119
|
+
connectivityKey: "net:VCC",
|
|
120
|
+
x: 0,
|
|
121
|
+
y: 0,
|
|
122
|
+
radius: 0.5,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
27
125
|
}
|
|
28
126
|
|
|
29
|
-
|
|
30
|
-
|
|
127
|
+
const output = new CopperPourPipelineSolver(input).getOutput()
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Supported input pad shapes are `rect`, `circle`, `pill`, `trace`, and `polygon`.
|
|
131
|
+
Use the same `connectivityKey` as the pour for pads/traces that should connect to
|
|
132
|
+
the copper island; use a different key for blockers that should be cleared.
|
|
133
|
+
|
|
134
|
+
## Output
|
|
135
|
+
|
|
136
|
+
`getOutput()` returns:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
interface PipelineOutput {
|
|
140
|
+
brep_shapes: BRepShape[]
|
|
31
141
|
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Each B-Rep shape is compatible with Circuit JSON copper pour data:
|
|
32
145
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
146
|
+
```ts
|
|
147
|
+
interface BRepShape {
|
|
148
|
+
outer_ring: {
|
|
149
|
+
vertices: Array<{ x: number; y: number; bulge?: number }>
|
|
150
|
+
}
|
|
151
|
+
inner_rings: Array<{
|
|
152
|
+
vertices: Array<{ x: number; y: number; bulge?: number }>
|
|
153
|
+
}>
|
|
36
154
|
}
|
|
37
155
|
```
|
|
156
|
+
|
|
157
|
+
## Development
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
bun install
|
|
161
|
+
bun run build
|
|
162
|
+
bun test
|
|
163
|
+
bun start
|
|
164
|
+
bun run build:site
|
|
165
|
+
```
|
package/cosmos.config.json
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BasePipelineSolver } from '@tscircuit/solver-utils';
|
|
2
2
|
import { Bounds, Point } from '@tscircuit/math-utils';
|
|
3
|
-
import { BRepShape,
|
|
3
|
+
import { BRepShape, LayerRef, Point as Point$1, AnyCircuitElement } from 'circuit-json';
|
|
4
4
|
|
|
5
5
|
interface InputPourRegion {
|
|
6
6
|
shape: "rect";
|
|
@@ -65,14 +65,24 @@ declare class CopperPourPipelineSolver extends BasePipelineSolver<InputProblem>
|
|
|
65
65
|
|
|
66
66
|
declare const initializeManifoldGeometry: () => Promise<void>;
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
interface ConvertCircuitJsonToInputProblemOptions {
|
|
69
69
|
layer: LayerRef;
|
|
70
|
-
|
|
70
|
+
subcircuit_id?: string;
|
|
71
|
+
source_net_id?: string;
|
|
72
|
+
source_net_name?: string;
|
|
73
|
+
subcircuit_connectivity_map_key?: string;
|
|
74
|
+
/**
|
|
75
|
+
* @deprecated Use subcircuit_connectivity_map_key, source_net_id, or
|
|
76
|
+
* source_net_name. Generated connectivity-map ids are intentionally rejected.
|
|
77
|
+
*/
|
|
78
|
+
pour_connectivity_key?: string;
|
|
71
79
|
pad_margin: number;
|
|
72
80
|
trace_margin: number;
|
|
73
81
|
board_edge_margin?: number;
|
|
74
82
|
cutout_margin?: number;
|
|
75
83
|
outline?: Point$1[];
|
|
76
|
-
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
declare const convertCircuitJsonToInputProblem: (circuitJson: AnyCircuitElement[], options: ConvertCircuitJsonToInputProblemOptions) => InputProblem;
|
|
77
87
|
|
|
78
|
-
export { type BaseInputPad, CopperPourPipelineSolver, type InputCircularPad, type InputPad, type InputPillPad, type InputPolygonPad, type InputPourRegion, type InputProblem, type InputRectPad, type InputTracePad, type PipelineOutput, convertCircuitJsonToInputProblem, initializeManifoldGeometry };
|
|
88
|
+
export { type BaseInputPad, type ConvertCircuitJsonToInputProblemOptions, CopperPourPipelineSolver, type InputCircularPad, type InputPad, type InputPillPad, type InputPolygonPad, type InputPourRegion, type InputProblem, type InputRectPad, type InputTracePad, type PipelineOutput, convertCircuitJsonToInputProblem, initializeManifoldGeometry };
|
package/dist/index.js
CHANGED
|
@@ -537,19 +537,263 @@ var CopperPourPipelineSolver = class extends BasePipelineSolver {
|
|
|
537
537
|
|
|
538
538
|
// lib/circuit-json/convert-circuit-json-to-input-problem.ts
|
|
539
539
|
import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
|
|
540
|
+
|
|
541
|
+
// lib/circuit-json/buildSubcircuitConnectivityLookup.ts
|
|
542
|
+
import { getElementId } from "@tscircuit/circuit-json-util";
|
|
543
|
+
|
|
544
|
+
// lib/circuit-json/getElementSubcircuitConnectivityKey.ts
|
|
545
|
+
var getElementSubcircuitConnectivityKey = (element) => {
|
|
546
|
+
const key = element.subcircuit_connectivity_map_key;
|
|
547
|
+
return typeof key === "string" && key.length > 0 ? key : void 0;
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// lib/circuit-json/buildSubcircuitConnectivityLookup.ts
|
|
551
|
+
var getElementSubcircuitId = (element) => {
|
|
552
|
+
const subcircuitId = element.subcircuit_id;
|
|
553
|
+
return typeof subcircuitId === "string" && subcircuitId.length > 0 ? subcircuitId : void 0;
|
|
554
|
+
};
|
|
555
|
+
var getScopedSubcircuitConnectivityKey = (subcircuitId, subcircuitConnectivityMapKey) => subcircuitId ? `subcircuit:${subcircuitId}:connectivity:${subcircuitConnectivityMapKey}` : subcircuitConnectivityMapKey;
|
|
556
|
+
var getDescendantSubcircuitIds = (circuitJson, rootSubcircuitId) => {
|
|
557
|
+
if (!rootSubcircuitId) return void 0;
|
|
558
|
+
const sourceGroupIdToSubcircuitId = {};
|
|
559
|
+
for (const element of circuitJson) {
|
|
560
|
+
if (element.type !== "source_group") continue;
|
|
561
|
+
const sourceGroupId = element.source_group_id;
|
|
562
|
+
const subcircuitId = getElementSubcircuitId(element);
|
|
563
|
+
if (typeof sourceGroupId === "string" && subcircuitId) {
|
|
564
|
+
sourceGroupIdToSubcircuitId[sourceGroupId] = subcircuitId;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const descendantSubcircuitIds = /* @__PURE__ */ new Set([rootSubcircuitId]);
|
|
568
|
+
let changed = true;
|
|
569
|
+
while (changed) {
|
|
570
|
+
changed = false;
|
|
571
|
+
for (const element of circuitJson) {
|
|
572
|
+
if (element.type !== "source_group") continue;
|
|
573
|
+
const subcircuitId = getElementSubcircuitId(element);
|
|
574
|
+
if (!subcircuitId || descendantSubcircuitIds.has(subcircuitId)) continue;
|
|
575
|
+
const parentSubcircuitId = element.parent_subcircuit_id;
|
|
576
|
+
const parentSourceGroupId = element.parent_source_group_id;
|
|
577
|
+
const parentSourceGroupSubcircuitId = typeof parentSourceGroupId === "string" ? sourceGroupIdToSubcircuitId[parentSourceGroupId] : void 0;
|
|
578
|
+
if (typeof parentSubcircuitId === "string" && descendantSubcircuitIds.has(parentSubcircuitId) || parentSourceGroupSubcircuitId && descendantSubcircuitIds.has(parentSourceGroupSubcircuitId)) {
|
|
579
|
+
descendantSubcircuitIds.add(subcircuitId);
|
|
580
|
+
changed = true;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return descendantSubcircuitIds;
|
|
585
|
+
};
|
|
586
|
+
var buildSubcircuitConnectivityLookup = (circuitJson, globalConnectivityMap, rootSubcircuitId) => {
|
|
587
|
+
const descendantSubcircuitIds = getDescendantSubcircuitIds(
|
|
588
|
+
circuitJson,
|
|
589
|
+
rootSubcircuitId
|
|
590
|
+
);
|
|
591
|
+
const idToSubcircuitConnectivityKey = {};
|
|
592
|
+
const idToSubcircuitId = {};
|
|
593
|
+
const scopedKeyToIds = {};
|
|
594
|
+
const localSubcircuitConnectivityKeys = /* @__PURE__ */ new Set();
|
|
595
|
+
for (const element of circuitJson) {
|
|
596
|
+
const id = getElementId(element);
|
|
597
|
+
const key = getElementSubcircuitConnectivityKey(element);
|
|
598
|
+
const subcircuitId = getElementSubcircuitId(element);
|
|
599
|
+
if (id) {
|
|
600
|
+
idToSubcircuitId[id] = subcircuitId;
|
|
601
|
+
}
|
|
602
|
+
if (descendantSubcircuitIds && (!subcircuitId || !descendantSubcircuitIds.has(subcircuitId))) {
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
if (id && key) {
|
|
606
|
+
const scopedKey = getScopedSubcircuitConnectivityKey(subcircuitId, key);
|
|
607
|
+
idToSubcircuitConnectivityKey[id] = scopedKey;
|
|
608
|
+
localSubcircuitConnectivityKeys.add(key);
|
|
609
|
+
scopedKeyToIds[scopedKey] ??= [];
|
|
610
|
+
scopedKeyToIds[scopedKey].push(id);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
const generatedNetIdToSubcircuitConnectivityKey = {};
|
|
614
|
+
for (const [generatedNetId, connectedIds] of Object.entries(
|
|
615
|
+
globalConnectivityMap.netMap
|
|
616
|
+
)) {
|
|
617
|
+
const connectedSubcircuitKeys = new Set(
|
|
618
|
+
connectedIds.map((id) => idToSubcircuitConnectivityKey[id]).filter((key) => Boolean(key))
|
|
619
|
+
);
|
|
620
|
+
const subcircuitKey = Array.from(connectedSubcircuitKeys).sort()[0];
|
|
621
|
+
if (subcircuitKey) {
|
|
622
|
+
generatedNetIdToSubcircuitConnectivityKey[generatedNetId] = subcircuitKey;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const subcircuitConnectivityMap = {};
|
|
626
|
+
for (const [scopedKey, ids] of Object.entries(scopedKeyToIds)) {
|
|
627
|
+
const resolvedKeys = /* @__PURE__ */ new Set();
|
|
628
|
+
for (const id of ids) {
|
|
629
|
+
const generatedNetId = globalConnectivityMap.getNetConnectedToId(id);
|
|
630
|
+
const resolvedKey2 = generatedNetId ? generatedNetIdToSubcircuitConnectivityKey[generatedNetId] : void 0;
|
|
631
|
+
resolvedKeys.add(resolvedKey2 ?? scopedKey);
|
|
632
|
+
}
|
|
633
|
+
if (resolvedKeys.size > 1) {
|
|
634
|
+
throw new Error(
|
|
635
|
+
`subcircuit_connectivity_map_key "${scopedKey}" maps to multiple global connectivity keys: ${Array.from(resolvedKeys).join(", ")}`
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
const resolvedKey = resolvedKeys.values().next().value;
|
|
639
|
+
if (resolvedKey) {
|
|
640
|
+
subcircuitConnectivityMap[scopedKey] = resolvedKey;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
knownSubcircuitConnectivityKeys: /* @__PURE__ */ new Set([
|
|
645
|
+
...Object.keys(scopedKeyToIds),
|
|
646
|
+
...localSubcircuitConnectivityKeys
|
|
647
|
+
]),
|
|
648
|
+
descendantSubcircuitIds,
|
|
649
|
+
getScopedSubcircuitConnectivityKey,
|
|
650
|
+
getElementSubcircuitId,
|
|
651
|
+
resolveSubcircuitConnectivityKey(subcircuitConnectivityMapKey, subcircuitId) {
|
|
652
|
+
const matchingScopedKeys = Object.keys(scopedKeyToIds).filter(
|
|
653
|
+
(scopedKey) => scopedKey === getScopedSubcircuitConnectivityKey(
|
|
654
|
+
subcircuitId,
|
|
655
|
+
subcircuitConnectivityMapKey
|
|
656
|
+
)
|
|
657
|
+
);
|
|
658
|
+
if (subcircuitId && matchingScopedKeys.length === 0 && descendantSubcircuitIds) {
|
|
659
|
+
matchingScopedKeys.push(
|
|
660
|
+
...Object.keys(scopedKeyToIds).filter(
|
|
661
|
+
(scopedKey) => scopedKey.endsWith(`:connectivity:${subcircuitConnectivityMapKey}`)
|
|
662
|
+
)
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
if (!subcircuitId) {
|
|
666
|
+
matchingScopedKeys.push(
|
|
667
|
+
...Object.keys(scopedKeyToIds).filter(
|
|
668
|
+
(scopedKey) => scopedKey.endsWith(`:connectivity:${subcircuitConnectivityMapKey}`)
|
|
669
|
+
)
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
const uniqueMatchingScopedKeys = Array.from(new Set(matchingScopedKeys));
|
|
673
|
+
if (uniqueMatchingScopedKeys.length === 0) {
|
|
674
|
+
if (subcircuitId && descendantSubcircuitIds) {
|
|
675
|
+
throw new Error(
|
|
676
|
+
`No subcircuit_connectivity_map_key "${subcircuitConnectivityMapKey}" found in subcircuit "${subcircuitId}" or its child subcircuits.`
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
return getScopedSubcircuitConnectivityKey(
|
|
680
|
+
subcircuitId,
|
|
681
|
+
subcircuitConnectivityMapKey
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
if (uniqueMatchingScopedKeys.length > 1) {
|
|
685
|
+
throw new Error(
|
|
686
|
+
`subcircuit_connectivity_map_key "${subcircuitConnectivityMapKey}" exists in multiple subcircuits. Pass subcircuit_id to disambiguate.`
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
return subcircuitConnectivityMap[uniqueMatchingScopedKeys[0]] ?? uniqueMatchingScopedKeys[0];
|
|
690
|
+
},
|
|
691
|
+
getSubcircuitConnectivityKeyForId(id) {
|
|
692
|
+
if (descendantSubcircuitIds) {
|
|
693
|
+
const subcircuitId = idToSubcircuitId[id];
|
|
694
|
+
if (!subcircuitId || !descendantSubcircuitIds.has(subcircuitId)) {
|
|
695
|
+
return void 0;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
const directKey = idToSubcircuitConnectivityKey[id];
|
|
699
|
+
const generatedNetId = globalConnectivityMap.getNetConnectedToId(id);
|
|
700
|
+
if (!generatedNetId) {
|
|
701
|
+
return directKey ? subcircuitConnectivityMap[directKey] ?? directKey : void 0;
|
|
702
|
+
}
|
|
703
|
+
return generatedNetIdToSubcircuitConnectivityKey[generatedNetId] ?? (directKey ? subcircuitConnectivityMap[directKey] : void 0) ?? directKey;
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
// lib/circuit-json/resolvePourConnectivityKey.ts
|
|
709
|
+
var resolvePourConnectivityKey = (circuitJson, options, subcircuitConnectivityMap) => {
|
|
710
|
+
if (options.subcircuit_connectivity_map_key) {
|
|
711
|
+
return subcircuitConnectivityMap.resolveSubcircuitConnectivityKey(
|
|
712
|
+
options.subcircuit_connectivity_map_key,
|
|
713
|
+
options.subcircuit_id
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
if (options.source_net_id) {
|
|
717
|
+
const sourceNet = circuitJson.find(
|
|
718
|
+
(element) => element.type === "source_net" && element.source_net_id === options.source_net_id
|
|
719
|
+
);
|
|
720
|
+
if (!sourceNet) {
|
|
721
|
+
throw new Error(`No source_net found with id "${options.source_net_id}"`);
|
|
722
|
+
}
|
|
723
|
+
if (!sourceNet.subcircuit_connectivity_map_key) {
|
|
724
|
+
throw new Error(
|
|
725
|
+
`source_net "${options.source_net_id}" has no subcircuit_connectivity_map_key`
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
return subcircuitConnectivityMap.resolveSubcircuitConnectivityKey(
|
|
729
|
+
sourceNet.subcircuit_connectivity_map_key,
|
|
730
|
+
sourceNet.subcircuit_id ?? options.subcircuit_id
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
if (options.source_net_name) {
|
|
734
|
+
const sourceNet = circuitJson.find(
|
|
735
|
+
(element) => element.type === "source_net" && element.name === options.source_net_name && (!options.subcircuit_id || Boolean(
|
|
736
|
+
subcircuitConnectivityMap.descendantSubcircuitIds?.has(
|
|
737
|
+
element.subcircuit_id ?? ""
|
|
738
|
+
)
|
|
739
|
+
))
|
|
740
|
+
);
|
|
741
|
+
if (!sourceNet) {
|
|
742
|
+
throw new Error(
|
|
743
|
+
`No source_net found with name "${options.source_net_name}"`
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
if (!sourceNet.subcircuit_connectivity_map_key) {
|
|
747
|
+
throw new Error(
|
|
748
|
+
`source_net "${options.source_net_name}" has no subcircuit_connectivity_map_key`
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
return subcircuitConnectivityMap.resolveSubcircuitConnectivityKey(
|
|
752
|
+
sourceNet.subcircuit_connectivity_map_key,
|
|
753
|
+
sourceNet.subcircuit_id ?? options.subcircuit_id
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
if (options.pour_connectivity_key) {
|
|
757
|
+
if (!subcircuitConnectivityMap.knownSubcircuitConnectivityKeys.has(
|
|
758
|
+
options.pour_connectivity_key
|
|
759
|
+
)) {
|
|
760
|
+
throw new Error(
|
|
761
|
+
`pour_connectivity_key must be a subcircuit_connectivity_map_key. Use subcircuit_connectivity_map_key, source_net_id, or source_net_name instead of a generated connectivity-map id.`
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
return subcircuitConnectivityMap.resolveSubcircuitConnectivityKey(
|
|
765
|
+
options.pour_connectivity_key,
|
|
766
|
+
options.subcircuit_id
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
throw new Error(
|
|
770
|
+
"Copper pour requires source_net_id, source_net_name, or subcircuit_connectivity_map_key"
|
|
771
|
+
);
|
|
772
|
+
};
|
|
773
|
+
|
|
774
|
+
// lib/circuit-json/convert-circuit-json-to-input-problem.ts
|
|
540
775
|
var convertCircuitJsonToInputProblem = (circuitJson, options) => {
|
|
541
776
|
const pcb_board = circuitJson.find((e) => e.type === "pcb_board");
|
|
542
777
|
if (!pcb_board) throw new Error("No pcb_board found in circuit json");
|
|
543
|
-
const
|
|
778
|
+
const globalConnectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson);
|
|
779
|
+
const subcircuitConnectivityMap = buildSubcircuitConnectivityLookup(
|
|
780
|
+
circuitJson,
|
|
781
|
+
globalConnectivityMap,
|
|
782
|
+
options.subcircuit_id
|
|
783
|
+
);
|
|
784
|
+
const pourConnectivityKey = resolvePourConnectivityKey(
|
|
785
|
+
circuitJson,
|
|
786
|
+
options,
|
|
787
|
+
subcircuitConnectivityMap
|
|
788
|
+
);
|
|
789
|
+
const { getSubcircuitConnectivityKeyForId } = subcircuitConnectivityMap;
|
|
544
790
|
const pads = [];
|
|
545
791
|
for (const elm of circuitJson) {
|
|
546
792
|
if (elm.type === "pcb_smtpad") {
|
|
547
793
|
const smtpad = elm;
|
|
548
794
|
if (smtpad.layer !== options.layer) continue;
|
|
549
795
|
let connectivityKey;
|
|
550
|
-
connectivityKey =
|
|
551
|
-
smtpad.pcb_smtpad_id
|
|
552
|
-
);
|
|
796
|
+
connectivityKey = getSubcircuitConnectivityKeyForId(smtpad.pcb_smtpad_id);
|
|
553
797
|
if (!connectivityKey) {
|
|
554
798
|
connectivityKey = `unconnected:${smtpad.pcb_smtpad_id}`;
|
|
555
799
|
}
|
|
@@ -593,7 +837,7 @@ var convertCircuitJsonToInputProblem = (circuitJson, options) => {
|
|
|
593
837
|
} else if (elm.type === "pcb_plated_hole") {
|
|
594
838
|
const platedHole = elm;
|
|
595
839
|
if (!platedHole.layers.includes(options.layer)) continue;
|
|
596
|
-
let connectivityKey =
|
|
840
|
+
let connectivityKey = getSubcircuitConnectivityKeyForId(
|
|
597
841
|
platedHole.pcb_plated_hole_id
|
|
598
842
|
);
|
|
599
843
|
if (!connectivityKey) {
|
|
@@ -681,7 +925,7 @@ var convertCircuitJsonToInputProblem = (circuitJson, options) => {
|
|
|
681
925
|
} else if (elm.type === "pcb_via") {
|
|
682
926
|
const via = elm;
|
|
683
927
|
if (!via.layers.includes(options.layer)) continue;
|
|
684
|
-
const connectivityKey =
|
|
928
|
+
const connectivityKey = getSubcircuitConnectivityKeyForId(via.pcb_via_id) ?? `unconnected-via:${via.pcb_via_id}`;
|
|
685
929
|
pads.push({
|
|
686
930
|
shape: "circle",
|
|
687
931
|
padId: via.pcb_via_id,
|
|
@@ -693,7 +937,7 @@ var convertCircuitJsonToInputProblem = (circuitJson, options) => {
|
|
|
693
937
|
});
|
|
694
938
|
} else if (elm.type === "pcb_trace") {
|
|
695
939
|
const trace = elm;
|
|
696
|
-
const connectivityKey =
|
|
940
|
+
const connectivityKey = getSubcircuitConnectivityKeyForId(
|
|
697
941
|
trace.pcb_trace_id
|
|
698
942
|
);
|
|
699
943
|
if (!connectivityKey) continue;
|
|
@@ -752,7 +996,7 @@ var convertCircuitJsonToInputProblem = (circuitJson, options) => {
|
|
|
752
996
|
layer: options.layer,
|
|
753
997
|
bounds,
|
|
754
998
|
outline,
|
|
755
|
-
connectivityKey:
|
|
999
|
+
connectivityKey: pourConnectivityKey,
|
|
756
1000
|
padMargin: options.pad_margin,
|
|
757
1001
|
traceMargin: options.trace_margin,
|
|
758
1002
|
board_edge_margin: options.board_edge_margin ?? 0,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { LayerRef, Point } from "circuit-json"
|
|
2
|
+
|
|
3
|
+
export interface ConvertCircuitJsonToInputProblemOptions {
|
|
4
|
+
layer: LayerRef
|
|
5
|
+
subcircuit_id?: string
|
|
6
|
+
source_net_id?: string
|
|
7
|
+
source_net_name?: string
|
|
8
|
+
subcircuit_connectivity_map_key?: string
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated Use subcircuit_connectivity_map_key, source_net_id, or
|
|
11
|
+
* source_net_name. Generated connectivity-map ids are intentionally rejected.
|
|
12
|
+
*/
|
|
13
|
+
pour_connectivity_key?: string
|
|
14
|
+
pad_margin: number
|
|
15
|
+
trace_margin: number
|
|
16
|
+
board_edge_margin?: number
|
|
17
|
+
cutout_margin?: number
|
|
18
|
+
outline?: Point[]
|
|
19
|
+
}
|