@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 CHANGED
@@ -1,37 +1,165 @@
1
1
  # @tscircuit/copper-pour-solver
2
2
 
3
- Solves for copper pour polygons
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
- ```tsx
6
- import { CopperPourPipelineSolver } from "@tscircuit/copper-pour-solver"
6
+ ## Install
7
7
 
8
- const solver = new CopperPourPipelineSolver({
9
- // Circuit JSON including pcb_plated_hole, pcb_hole, pcb_smtpad, pcb_trace, pcb_keepout etc.
10
- circuitJson
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.solve()
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
- solver.getOutput()
16
- // { brepShapes }
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
- ## B-Rep Shapes
60
+ You can also pass the source net's stable `subcircuit_connectivity_map_key`
61
+ directly:
20
62
 
21
- We use the following representation for 2D b-rep shapes
63
+ ```ts
64
+ const gnd = circuitJson.find(
65
+ (element) => element.type === "source_net" && element.name === "GND",
66
+ )
22
67
 
23
- ```tsx
24
- interface BRepShape {
25
- outerRing: Ring // The outer boundary
26
- innerRings: Ring // The inner cutouts
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
- interface Ring {
30
- cwVertices: PointWithBulge[]
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
- interface PointWithBulge {
34
- x: number
35
- y: number
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
+ ```
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "$schema": "http://json.schemastore.org/cosmos-config",
3
3
  "plugins": ["react-cosmos-plugin-vite"],
4
- "fixtureFileSuffix": "page"
4
+ "fixtureFileSuffix": "page",
5
+ "exportPath": "cosmos-export"
5
6
  }
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, AnyCircuitElement, LayerRef, Point as Point$1 } from 'circuit-json';
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
- declare const convertCircuitJsonToInputProblem: (circuitJson: AnyCircuitElement[], options: {
68
+ interface ConvertCircuitJsonToInputProblemOptions {
69
69
  layer: LayerRef;
70
- pour_connectivity_key: string;
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
- }) => InputProblem;
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 connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson);
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 = connectivityMap.getNetConnectedToId(
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 = connectivityMap.getNetConnectedToId(
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 = connectivityMap.getNetConnectedToId(via.pcb_via_id) ?? `unconnected-via:${via.pcb_via_id}`;
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 = connectivityMap.getNetConnectedToId(
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: options.pour_connectivity_key,
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
+ }