@tscircuit/copper-pour-solver 0.0.32 → 0.0.34

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.
@@ -0,0 +1,8 @@
1
+ import type { AnyCircuitElement } from "circuit-json"
2
+
3
+ export const getElementSubcircuitConnectivityKey = (
4
+ element: AnyCircuitElement,
5
+ ): string | undefined => {
6
+ const key = (element as any).subcircuit_connectivity_map_key
7
+ return typeof key === "string" && key.length > 0 ? key : undefined
8
+ }
@@ -0,0 +1,61 @@
1
+ import type { AnyCircuitElement, SourceNet } from "circuit-json"
2
+ import type { ConvertCircuitJsonToInputProblemOptions } from "./ConvertCircuitJsonToInputProblemOptions"
3
+
4
+ export const resolvePourConnectivityKey = (
5
+ circuitJson: AnyCircuitElement[],
6
+ options: ConvertCircuitJsonToInputProblemOptions,
7
+ knownSubcircuitConnectivityKeys: Set<string>,
8
+ ): string => {
9
+ if (options.subcircuit_connectivity_map_key) {
10
+ return options.subcircuit_connectivity_map_key
11
+ }
12
+
13
+ if (options.source_net_id) {
14
+ const sourceNet = circuitJson.find(
15
+ (element): element is SourceNet =>
16
+ element.type === "source_net" &&
17
+ element.source_net_id === options.source_net_id,
18
+ )
19
+ if (!sourceNet) {
20
+ throw new Error(`No source_net found with id "${options.source_net_id}"`)
21
+ }
22
+ if (!sourceNet.subcircuit_connectivity_map_key) {
23
+ throw new Error(
24
+ `source_net "${options.source_net_id}" has no subcircuit_connectivity_map_key`,
25
+ )
26
+ }
27
+ return sourceNet.subcircuit_connectivity_map_key
28
+ }
29
+
30
+ if (options.source_net_name) {
31
+ const sourceNet = circuitJson.find(
32
+ (element): element is SourceNet =>
33
+ element.type === "source_net" &&
34
+ element.name === options.source_net_name,
35
+ )
36
+ if (!sourceNet) {
37
+ throw new Error(
38
+ `No source_net found with name "${options.source_net_name}"`,
39
+ )
40
+ }
41
+ if (!sourceNet.subcircuit_connectivity_map_key) {
42
+ throw new Error(
43
+ `source_net "${options.source_net_name}" has no subcircuit_connectivity_map_key`,
44
+ )
45
+ }
46
+ return sourceNet.subcircuit_connectivity_map_key
47
+ }
48
+
49
+ if (options.pour_connectivity_key) {
50
+ if (!knownSubcircuitConnectivityKeys.has(options.pour_connectivity_key)) {
51
+ throw new Error(
52
+ `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.`,
53
+ )
54
+ }
55
+ return options.pour_connectivity_key
56
+ }
57
+
58
+ throw new Error(
59
+ "Copper pour requires source_net_id, source_net_name, or subcircuit_connectivity_map_key",
60
+ )
61
+ }
package/lib/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./solvers/CopperPourPipelineSolver"
2
2
  export { initializeManifoldGeometry } from "./solvers/copper-pour/manifold-runtime"
3
+ export type { ConvertCircuitJsonToInputProblemOptions } from "./circuit-json/ConvertCircuitJsonToInputProblemOptions"
3
4
  export * from "./circuit-json/convert-circuit-json-to-input-problem"
4
5
  export * from "./types"
@@ -55,3 +55,56 @@ export const segmentToPolygon = (
55
55
  y: centerY + point.x * sinAngle + point.y * cosAngle,
56
56
  }))
57
57
  }
58
+
59
+ export const pillToPolygon = (
60
+ center: Point,
61
+ width: number,
62
+ height: number,
63
+ radius: number,
64
+ ccwRotation = 0,
65
+ numSegments = 32,
66
+ ): PolygonRing => {
67
+ if (radius <= 0) return []
68
+
69
+ const isVertical = height >= width
70
+ const centerlineLength = Math.max(width, height) - 2 * radius
71
+ const halfCenterline = Math.max(0, centerlineLength / 2)
72
+ const baseAngle = isVertical ? Math.PI / 2 : 0
73
+ const rotation = baseAngle + (ccwRotation * Math.PI) / 180
74
+
75
+ const cosAngle = Math.cos(rotation)
76
+ const sinAngle = Math.sin(rotation)
77
+ const start = {
78
+ x: center.x - halfCenterline * cosAngle,
79
+ y: center.y - halfCenterline * sinAngle,
80
+ }
81
+ const end = {
82
+ x: center.x + halfCenterline * cosAngle,
83
+ y: center.y + halfCenterline * sinAngle,
84
+ }
85
+
86
+ if (halfCenterline === 0) {
87
+ return circleToPolygon(center, radius, numSegments)
88
+ }
89
+
90
+ const points: PolygonRing = []
91
+ const halfSegments = Math.max(4, Math.floor(numSegments / 2))
92
+
93
+ for (let i = 0; i <= halfSegments; i++) {
94
+ const angle = rotation - Math.PI / 2 + (i / halfSegments) * Math.PI
95
+ points.push({
96
+ x: end.x + radius * Math.cos(angle),
97
+ y: end.y + radius * Math.sin(angle),
98
+ })
99
+ }
100
+
101
+ for (let i = 0; i <= halfSegments; i++) {
102
+ const angle = rotation + Math.PI / 2 + (i / halfSegments) * Math.PI
103
+ points.push({
104
+ x: start.x + radius * Math.cos(angle),
105
+ y: start.y + radius * Math.sin(angle),
106
+ })
107
+ }
108
+
109
+ return points
110
+ }
@@ -2,6 +2,7 @@ import type { Point } from "@tscircuit/math-utils"
2
2
  import type {
3
3
  InputCircularPad,
4
4
  InputPad,
5
+ InputPillPad,
5
6
  InputPolygonPad,
6
7
  InputRectPad,
7
8
  InputTracePad,
@@ -10,6 +11,7 @@ import { offsetPolygon } from "./manifold-geometry-adapter"
10
11
  import {
11
12
  boxToPolygon,
12
13
  circleToPolygon,
14
+ pillToPolygon,
13
15
  segmentToPolygon,
14
16
  } from "./polygon-primitives"
15
17
  import { normalizeRing, type PolygonRing } from "./polygon-ring"
@@ -23,6 +25,7 @@ const isTracePad = (pad: InputPad): pad is InputTracePad =>
23
25
  pad.shape === "trace"
24
26
  const isCircularPad = (pad: InputPad): pad is InputCircularPad =>
25
27
  pad.shape === "circle"
28
+ const isPillPad = (pad: InputPad): pad is InputPillPad => pad.shape === "pill"
26
29
  const isPolygonPad = (pad: InputPad): pad is InputPolygonPad =>
27
30
  pad.shape === "polygon"
28
31
 
@@ -111,6 +114,20 @@ export const processObstaclesForPour = (
111
114
  continue
112
115
  }
113
116
 
117
+ if (isPillPad(pad)) {
118
+ const margin = isHoleOrCutout ? (cutoutMargin ?? 0) : padMargin
119
+ polygonsToSubtract.push(
120
+ pillToPolygon(
121
+ { x: pad.x, y: pad.y },
122
+ pad.width + margin * 2,
123
+ pad.height + margin * 2,
124
+ pad.radius + margin,
125
+ pad.ccwRotation,
126
+ ),
127
+ )
128
+ continue
129
+ }
130
+
114
131
  if (isRectPad(pad)) {
115
132
  const margin = isHoleOrCutout ? (cutoutMargin ?? 0) : padMargin
116
133
  const { bounds } = pad
package/lib/types.ts CHANGED
@@ -31,6 +31,16 @@ export interface InputCircularPad extends BaseInputPad {
31
31
  radius: number
32
32
  }
33
33
 
34
+ export interface InputPillPad extends BaseInputPad {
35
+ shape: "pill"
36
+ x: number
37
+ y: number
38
+ width: number
39
+ height: number
40
+ radius: number
41
+ ccwRotation: number
42
+ }
43
+
34
44
  export interface InputTracePad extends BaseInputPad {
35
45
  shape: "trace"
36
46
  width: number
@@ -45,6 +55,7 @@ export interface InputPolygonPad extends BaseInputPad {
45
55
  export type InputPad =
46
56
  | InputRectPad
47
57
  | InputCircularPad
58
+ | InputPillPad
48
59
  | InputTracePad
49
60
  | InputPolygonPad
50
61
 
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "name": "@tscircuit/copper-pour-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.32",
4
+ "version": "0.0.34",
5
5
  "scripts": {
6
6
  "format": "biome format . --write",
7
7
  "format:check": "biome format .",
8
+ "start": "cosmos",
9
+ "cosmos": "react-cosmos",
10
+ "build:site": "cosmos-export",
8
11
  "build": "tsup-node lib/index.ts --dts --format esm"
9
12
  },
10
13
  "type": "module",
11
14
  "devDependencies": {
12
15
  "@biomejs/biome": "^2.2.4",
16
+ "@tscircuit/circuit-json-util": "^0.0.77",
13
17
  "@flatten-js/core": "^1.6.2",
14
18
  "@tscircuit/math-utils": "^0.0.25",
15
19
  "@tscircuit/solver-utils": "^0.0.14",
@@ -0,0 +1,214 @@
1
+ import type React from "react"
2
+
3
+ const codeSample = `import {
4
+ CopperPourPipelineSolver,
5
+ convertCircuitJsonToInputProblem,
6
+ initializeManifoldGeometry,
7
+ } from "@tscircuit/copper-pour-solver"
8
+
9
+ await initializeManifoldGeometry()
10
+
11
+ const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
12
+ layer: "top",
13
+ source_net_name: "GND",
14
+ pad_margin: 0.4,
15
+ trace_margin: 0.2,
16
+ board_edge_margin: 0.1,
17
+ cutout_margin: 0.2,
18
+ })
19
+
20
+ const solver = new CopperPourPipelineSolver(inputProblem)
21
+ const { brep_shapes } = solver.getOutput()`
22
+
23
+ const developmentSample = `bun install
24
+ bun run build
25
+ bun test
26
+ bun start
27
+ bun run build:site`
28
+
29
+ export default function WelcomePage() {
30
+ return (
31
+ <main style={styles.main}>
32
+ <header style={styles.header}>
33
+ <p style={styles.eyebrow}>tscircuit geometry</p>
34
+ <h1 style={styles.title}>@tscircuit/copper-pour-solver</h1>
35
+ <p style={styles.lede}>
36
+ Generate PCB copper pour B-Rep polygons from Circuit JSON or from a
37
+ direct geometry input format.
38
+ </p>
39
+ <nav style={styles.actions} aria-label="Project links">
40
+ <a style={{ ...styles.button, ...styles.primaryButton }} href="/">
41
+ Cosmos docs
42
+ </a>
43
+ <a
44
+ style={styles.button}
45
+ href="https://github.com/tscircuit/copper-pour-solver"
46
+ >
47
+ GitHub
48
+ </a>
49
+ <a
50
+ style={styles.button}
51
+ href="https://www.npmjs.com/package/@tscircuit/copper-pour-solver"
52
+ >
53
+ npm
54
+ </a>
55
+ </nav>
56
+ </header>
57
+
58
+ <section style={styles.section}>
59
+ <h2 style={styles.heading}>Install</h2>
60
+ <pre style={styles.pre}>
61
+ <code>bun add @tscircuit/copper-pour-solver</code>
62
+ </pre>
63
+ </section>
64
+
65
+ <section style={styles.section}>
66
+ <h2 style={styles.heading}>Use With Circuit JSON</h2>
67
+ <p style={styles.copy}>
68
+ Initialize the Manifold geometry runtime once, convert Circuit JSON
69
+ into a solver input problem by source net name, then read the
70
+ generated B-Rep shapes.
71
+ </p>
72
+ <pre style={styles.pre}>
73
+ <code>{codeSample}</code>
74
+ </pre>
75
+ </section>
76
+
77
+ <section style={styles.section}>
78
+ <h2 style={styles.heading}>What The Converter Reads</h2>
79
+ <div style={styles.grid}>
80
+ <div style={styles.item}>
81
+ <strong>Board geometry</strong>
82
+ <span>
83
+ Board bounds, board outlines, and optional pour outlines.
84
+ </span>
85
+ </div>
86
+ <div style={styles.item}>
87
+ <strong>Obstacles</strong>
88
+ <span>
89
+ SMT pads, plated holes, mechanical holes, vias, traces, and
90
+ cutouts.
91
+ </span>
92
+ </div>
93
+ <div style={styles.item}>
94
+ <strong>Connectivity</strong>
95
+ <span>
96
+ Source net ids, source net names, or stable
97
+ subcircuit_connectivity_map_key values.
98
+ </span>
99
+ </div>
100
+ <div style={styles.item}>
101
+ <strong>Clearances</strong>
102
+ <span>Pad, trace, board edge, and cutout margin controls.</span>
103
+ </div>
104
+ </div>
105
+ </section>
106
+
107
+ <section style={styles.section}>
108
+ <h2 style={styles.heading}>Development</h2>
109
+ <pre style={styles.pre}>
110
+ <code>{developmentSample}</code>
111
+ </pre>
112
+ </section>
113
+ </main>
114
+ )
115
+ }
116
+
117
+ const styles: Record<string, React.CSSProperties> = {
118
+ main: {
119
+ width: "min(980px, calc(100% - 32px))",
120
+ margin: "0 auto",
121
+ padding: "48px 0 64px",
122
+ color: "#17202a",
123
+ fontFamily:
124
+ 'Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
125
+ lineHeight: 1.55,
126
+ },
127
+ header: {
128
+ borderBottom: "1px solid #d7dee4",
129
+ paddingBottom: 28,
130
+ marginBottom: 32,
131
+ },
132
+ eyebrow: {
133
+ margin: "0 0 10px",
134
+ color: "#0b766e",
135
+ fontSize: 13,
136
+ fontWeight: 700,
137
+ textTransform: "uppercase",
138
+ letterSpacing: 0,
139
+ },
140
+ title: {
141
+ margin: "0 0 12px",
142
+ fontSize: "clamp(2rem, 7vw, 4rem)",
143
+ lineHeight: 1,
144
+ letterSpacing: 0,
145
+ },
146
+ lede: {
147
+ maxWidth: 760,
148
+ margin: "0 0 16px",
149
+ color: "#5f6f7a",
150
+ fontSize: "1.12rem",
151
+ },
152
+ actions: {
153
+ display: "flex",
154
+ flexWrap: "wrap",
155
+ gap: 10,
156
+ marginTop: 22,
157
+ },
158
+ button: {
159
+ display: "inline-flex",
160
+ alignItems: "center",
161
+ minHeight: 38,
162
+ padding: "7px 12px",
163
+ border: "1px solid #d7dee4",
164
+ borderRadius: 6,
165
+ textDecoration: "none",
166
+ color: "#17202a",
167
+ background: "#ffffff",
168
+ fontWeight: 650,
169
+ },
170
+ primaryButton: {
171
+ color: "#ffffff",
172
+ background: "#0b766e",
173
+ borderColor: "#0b766e",
174
+ },
175
+ section: {
176
+ marginTop: 34,
177
+ },
178
+ heading: {
179
+ margin: "0 0 12px",
180
+ fontSize: "1.35rem",
181
+ letterSpacing: 0,
182
+ },
183
+ copy: {
184
+ maxWidth: 760,
185
+ margin: "0 0 16px",
186
+ color: "#34444f",
187
+ },
188
+ pre: {
189
+ overflowX: "auto",
190
+ margin: "14px 0 24px",
191
+ padding: 16,
192
+ border: "1px solid #d7dee4",
193
+ borderRadius: 8,
194
+ background: "#f7f9fb",
195
+ color: "#10212d",
196
+ fontFamily:
197
+ '"SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace',
198
+ fontSize: 14,
199
+ },
200
+ grid: {
201
+ display: "grid",
202
+ gridTemplateColumns: "repeat(auto-fit, minmax(230px, 1fr))",
203
+ gap: 14,
204
+ margin: "18px 0 8px",
205
+ },
206
+ item: {
207
+ display: "grid",
208
+ gap: 4,
209
+ border: "1px solid #d7dee4",
210
+ borderRadius: 8,
211
+ padding: 14,
212
+ background: "#ffffff",
213
+ },
214
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="600"><style></style><rect class="boundary" x="0" y="0" fill="#000" width="800" height="600" data-type="pcb_background" data-pcb-layer="global"/><rect class="pcb-boundary" fill="none" stroke="#fff" stroke-width="0.3" x="50" y="50" width="700" height="500" data-type="pcb_boundary" data-pcb-layer="global"/><path class="pcb-board" d="M 50 550 L 750 550 L 750 50 L 50 50 Z" fill="none" stroke="rgba(255, 255, 255, 0.5)" stroke-width="5" data-type="pcb_board" data-pcb-layer="board"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="163.750155" y="399.999165" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="227.250155" y="399.999165" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="290.750155" y="399.999165" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="354.250155" y="399.999165" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="417.750155" y="399.999165" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="481.250155" y="399.999165" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="544.750155" y="399.999165" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="608.250155" y="399.999165" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="163.750155" y="112.750565" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="227.250155" y="112.750565" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="290.750155" y="112.750565" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="354.250155" y="112.750565" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="417.750155" y="112.750565" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="481.250155" y="112.750565" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="544.750155" y="112.750565" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="608.250155" y="112.750565" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="-13.999845" y="-43.625135" width="27.99969" height="87.25027" rx="13.999845" ry="13.999845" data-type="pcb_smtpad" data-pcb-layer="top" transform="translate(400 300) rotate(-90)"/><path class="pcb-copper-pour pcb-copper-pour-brep" d="M 750 550 L 50 550 L 50 50 L 750 50 L 750 550 Z M 177.75 207.50085 L 181.94439999999997 207.08769999999998 L 185.97765 205.86425 L 189.69465 203.87745 L 192.95270000000002 201.2037 L 195.62645 197.94565 L 197.61325 194.2286 L 198.83675 190.1954 L 199.24984999999998 186.00099999999998 L 199.24984999999998 126.75039999999998 L 198.83675 122.55600000000001 L 197.61325 118.52279999999999 L 195.62645 114.80574999999999 L 192.95270000000002 111.54769999999999 L 189.69465 108.87395000000001 L 185.97765 106.88714999999999 L 181.94439999999997 105.6637 L 177.75 105.25055 L 173.5556 105.6637 L 169.52235 106.88714999999999 L 165.80534999999998 108.87395000000001 L 162.5473 111.54769999999999 L 159.87355000000002 114.80574999999999 L 157.88674999999998 118.52279999999999 L 156.66324999999998 122.55600000000001 L 156.25015000000002 126.75039999999998 L 156.25015000000002 186.00099999999998 L 156.66324999999998 190.1954 L 157.88674999999998 194.2286 L 159.87355000000002 197.94565 L 162.5473 201.2037 L 165.80534999999998 203.87745 L 169.52235 205.86425 L 173.5556 207.08769999999998 L 177.75 207.50085 Z M 241.25 207.50085 L 245.4444 207.08769999999998 L 249.47764999999998 205.86425 L 253.19465000000002 203.87745 L 256.4527 201.2037 L 259.12645 197.94565 L 261.11325 194.2286 L 262.33675 190.1954 L 262.74985000000004 186.00099999999998 L 262.74985000000004 126.75039999999998 L 262.33675 122.55600000000001 L 261.11325 118.52279999999999 L 259.12645 114.80574999999999 L 256.4527 111.54769999999999 L 253.19465000000002 108.87395000000001 L 249.47764999999998 106.88714999999999 L 245.4444 105.6637 L 241.25 105.25055 L 237.0556 105.6637 L 233.02235 106.88714999999999 L 229.30535 108.87395000000001 L 226.0473 111.54769999999999 L 223.37355000000002 114.80574999999999 L 221.38675 118.52279999999999 L 220.16325 122.55600000000001 L 219.75015 126.75039999999998 L 219.75015 186.00099999999998 L 220.16325 190.1954 L 221.38675 194.2286 L 223.37355000000002 197.94565 L 226.0473 201.2037 L 229.30535 203.87745 L 233.02235 205.86425 L 237.0556 207.08769999999998 L 241.25 207.50085 Z M 304.75 207.50085 L 308.9444 207.08769999999998 L 312.97765 205.86425 L 316.69465 203.87745 L 319.9527 201.2037 L 322.62645 197.94565 L 324.61325 194.2286 L 325.83675 190.1954 L 326.24985 186.00099999999998 L 326.24985 126.75039999999998 L 325.83675 122.55600000000001 L 324.61325 118.52279999999999 L 322.62645 114.80574999999999 L 319.9527 111.54769999999999 L 316.69465 108.87395000000001 L 312.97765 106.88714999999999 L 308.9444 105.6637 L 304.75 105.25055 L 300.5556 105.6637 L 296.52235 106.88714999999999 L 292.80535 108.87395000000001 L 289.5473 111.54769999999999 L 286.87355 114.80574999999999 L 284.88675 118.52279999999999 L 283.66325 122.55600000000001 L 283.25015 126.75039999999998 L 283.25015 186.00099999999998 L 283.66325 190.1954 L 284.88675 194.2286 L 286.87355 197.94565 L 289.5473 201.2037 L 292.80535 203.87745 L 296.52235 205.86425 L 300.5556 207.08769999999998 L 304.75 207.50085 Z M 368.25 207.50085 L 372.4444 207.08769999999998 L 376.47765 205.86425 L 380.19465 203.87745 L 383.4527 201.2037 L 386.12645 197.94565 L 388.11325 194.2286 L 389.33675 190.1954 L 389.74985 186.00099999999998 L 389.74985 126.75039999999998 L 389.33675 122.55600000000001 L 388.11325 118.52279999999999 L 386.12645 114.80574999999999 L 383.4527 111.54769999999999 L 380.19465 108.87395000000001 L 376.47765 106.88714999999999 L 372.4444 105.6637 L 368.25 105.25055 L 364.0556 105.6637 L 360.02235 106.88714999999999 L 356.30535 108.87395000000001 L 353.0473 111.54769999999999 L 350.37355 114.80574999999999 L 348.38675 118.52279999999999 L 347.16325 122.55600000000001 L 346.75015 126.75039999999998 L 346.75015 186.00099999999998 L 347.16325 190.1954 L 348.38675 194.2286 L 350.37355 197.94565 L 353.0473 201.2037 L 356.30535 203.87745 L 360.02235 205.86425 L 364.0556 207.08769999999998 L 368.25 207.50085 Z M 431.75 207.50085 L 435.9444 207.08769999999998 L 439.97765 205.86425 L 443.69465 203.87745 L 446.9527 201.2037 L 449.62645 197.94565 L 451.61325 194.2286 L 452.83675 190.1954 L 453.24985 186.00099999999998 L 453.24985 126.75039999999998 L 452.83675 122.55600000000001 L 451.61325 118.52279999999999 L 449.62645 114.80574999999999 L 446.9527 111.54769999999999 L 443.69465 108.87395000000001 L 439.97765 106.88714999999999 L 435.9444 105.6637 L 431.75 105.25055 L 427.5556 105.6637 L 423.52235 106.88714999999999 L 419.80535 108.87395000000001 L 416.5473 111.54769999999999 L 413.87355 114.80574999999999 L 411.88675 118.52279999999999 L 410.66325 122.55600000000001 L 410.25015 126.75039999999998 L 410.25015 186.00099999999998 L 410.66325 190.1954 L 411.88675 194.2286 L 413.87355 197.94565 L 416.5473 201.2037 L 419.80535 203.87745 L 423.52235 205.86425 L 427.5556 207.08769999999998 L 431.75 207.50085 Z M 495.25 207.50085 L 499.4444 207.08769999999998 L 503.47765 205.86425 L 507.19465 203.87745 L 510.4527 201.2037 L 513.12645 197.94565 L 515.11325 194.2286 L 516.33675 190.1954 L 516.74985 186.00099999999998 L 516.74985 126.75039999999998 L 516.33675 122.55600000000001 L 515.11325 118.52279999999999 L 513.12645 114.80574999999999 L 510.4527 111.54769999999999 L 507.19465 108.87395000000001 L 503.47765 106.88714999999999 L 499.4444 105.6637 L 495.25 105.25055 L 491.0556 105.6637 L 487.02235 106.88714999999999 L 483.30535 108.87395000000001 L 480.0473 111.54769999999999 L 477.37355 114.80574999999999 L 475.38675 118.52279999999999 L 474.16325 122.55600000000001 L 473.75015 126.75039999999998 L 473.75015 186.00099999999998 L 474.16325 190.1954 L 475.38675 194.2286 L 477.37355 197.94565 L 480.0473 201.2037 L 483.30535 203.87745 L 487.02235 205.86425 L 491.0556 207.08769999999998 L 495.25 207.50085 Z M 558.75 207.50085 L 562.9444 207.08769999999998 L 566.97765 205.86425 L 570.69465 203.87745 L 573.9527 201.2037 L 576.62645 197.94565 L 578.61325 194.2286 L 579.8367499999999 190.1954 L 580.24985 186.00099999999998 L 580.24985 126.75039999999998 L 579.8367499999999 122.55600000000001 L 578.61325 118.52279999999999 L 576.62645 114.80574999999999 L 573.9527 111.54769999999999 L 570.69465 108.87395000000001 L 566.97765 106.88714999999999 L 562.9444 105.6637 L 558.75 105.25055 L 554.5556 105.6637 L 550.52235 106.88714999999999 L 546.80535 108.87395000000001 L 543.5473 111.54769999999999 L 540.87355 114.80574999999999 L 538.88675 118.52279999999999 L 537.6632500000001 122.55600000000001 L 537.25015 126.75039999999998 L 537.25015 186.00099999999998 L 537.6632500000001 190.1954 L 538.88675 194.2286 L 540.87355 197.94565 L 543.5473 201.2037 L 546.80535 203.87745 L 550.52235 205.86425 L 554.5556 207.08769999999998 L 558.75 207.50085 Z M 622.25 207.50085 L 626.4444 207.08769999999998 L 630.47765 205.86425 L 634.19465 203.87745 L 637.4527 201.2037 L 640.12645 197.94565 L 642.11325 194.2286 L 643.33675 190.1954 L 643.7498499999999 186.00099999999998 L 643.7498499999999 126.75039999999998 L 643.33675 122.55600000000001 L 642.11325 118.52279999999999 L 640.12645 114.80574999999999 L 637.4527 111.54769999999999 L 634.19465 108.87395000000001 L 630.47765 106.88714999999999 L 626.4444 105.6637 L 622.25 105.25055 L 618.0556 105.6637 L 614.02235 106.88714999999999 L 610.30535 108.87395000000001 L 607.0473 111.54769999999999 L 604.37355 114.80574999999999 L 602.38675 118.52279999999999 L 601.1632500000001 122.55600000000001 L 600.7501500000001 126.75039999999998 L 600.7501500000001 186.00099999999998 L 601.1632500000001 190.1954 L 602.38675 194.2286 L 604.37355 197.94565 L 607.0473 201.2037 L 610.30535 203.87745 L 614.02235 205.86425 L 618.0556 207.08769999999998 L 622.25 207.50085 Z M 370.3747 321.49985 L 429.6253 321.49985 L 433.8197 321.08675 L 437.8529 319.86325 L 441.56995 317.87645 L 444.828 315.2027 L 447.50175 311.94465 L 449.48855 308.22765 L 450.712 304.1944 L 451.12515 300 L 450.712 295.8056 L 449.48855 291.77235 L 447.50175 288.05535 L 444.828 284.7973 L 441.56995 282.12355 L 437.8529 280.13675 L 433.8197 278.91325 L 429.6253 278.50015 L 370.3747 278.50015 L 366.1803 278.91325 L 362.1471 280.13675 L 358.43005 282.12355 L 355.172 284.7973 L 352.49825 288.05535 L 350.51145 291.77235 L 349.288 295.8056 L 348.87485 300 L 349.288 304.1944 L 350.51145 308.22765 L 352.49825 311.94465 L 355.172 315.2027 L 358.43005 317.87645 L 362.1471 319.86325 L 366.1803 321.08675 L 370.3747 321.49985 Z M 177.75 494.74945 L 181.94439999999997 494.3363 L 185.97765 493.11285 L 189.69465 491.12604999999996 L 192.95270000000002 488.45230000000004 L 195.62645 485.19425 L 197.61325 481.47720000000004 L 198.83675 477.44399999999996 L 199.24984999999998 473.2496 L 199.24984999999998 413.999 L 198.83675 409.8046 L 197.61325 405.77139999999997 L 195.62645 402.05435 L 192.95270000000002 398.7963 L 189.69465 396.12255 L 185.97765 394.13575000000003 L 181.94439999999997 392.9123 L 177.75 392.49915 L 173.5556 392.9123 L 169.52235 394.13575000000003 L 165.80534999999998 396.12255 L 162.5473 398.7963 L 159.87355000000002 402.05435 L 157.88674999999998 405.77139999999997 L 156.66324999999998 409.8046 L 156.25015000000002 413.999 L 156.25015000000002 473.2496 L 156.66324999999998 477.44399999999996 L 157.88674999999998 481.47720000000004 L 159.87355000000002 485.19425 L 162.5473 488.45230000000004 L 165.80534999999998 491.12604999999996 L 169.52235 493.11285 L 173.5556 494.3363 L 177.75 494.74945 Z M 241.25 494.74945 L 245.4444 494.3363 L 249.47764999999998 493.11285 L 253.19465000000002 491.12604999999996 L 256.4527 488.45230000000004 L 259.12645 485.19425 L 261.11325 481.47720000000004 L 262.33675 477.44399999999996 L 262.74985000000004 473.2496 L 262.74985000000004 413.999 L 262.33675 409.8046 L 261.11325 405.77139999999997 L 259.12645 402.05435 L 256.4527 398.7963 L 253.19465000000002 396.12255 L 249.47764999999998 394.13575000000003 L 245.4444 392.9123 L 241.25 392.49915 L 237.0556 392.9123 L 233.02235 394.13575000000003 L 229.30535 396.12255 L 226.0473 398.7963 L 223.37355000000002 402.05435 L 221.38675 405.77139999999997 L 220.16325 409.8046 L 219.75015 413.999 L 219.75015 473.2496 L 220.16325 477.44399999999996 L 221.38675 481.47720000000004 L 223.37355000000002 485.19425 L 226.0473 488.45230000000004 L 229.30535 491.12604999999996 L 233.02235 493.11285 L 237.0556 494.3363 L 241.25 494.74945 Z M 304.75 494.74945 L 308.9444 494.3363 L 312.97765 493.11285 L 316.69465 491.12604999999996 L 319.9527 488.45230000000004 L 322.62645 485.19425 L 324.61325 481.47720000000004 L 325.83675 477.44399999999996 L 326.24985 473.2496 L 326.24985 413.999 L 325.83675 409.8046 L 324.61325 405.77139999999997 L 322.62645 402.05435 L 319.9527 398.7963 L 316.69465 396.12255 L 312.97765 394.13575000000003 L 308.9444 392.9123 L 304.75 392.49915 L 300.5556 392.9123 L 296.52235 394.13575000000003 L 292.80535 396.12255 L 289.5473 398.7963 L 286.87355 402.05435 L 284.88675 405.77139999999997 L 283.66325 409.8046 L 283.25015 413.999 L 283.25015 473.2496 L 283.66325 477.44399999999996 L 284.88675 481.47720000000004 L 286.87355 485.19425 L 289.5473 488.45230000000004 L 292.80535 491.12604999999996 L 296.52235 493.11285 L 300.5556 494.3363 L 304.75 494.74945 Z M 368.25 494.74945 L 372.4444 494.3363 L 376.47765 493.11285 L 380.19465 491.12604999999996 L 383.4527 488.45230000000004 L 386.12645 485.19425 L 388.11325 481.47720000000004 L 389.33675 477.44399999999996 L 389.74985 473.2496 L 389.74985 413.999 L 389.33675 409.8046 L 388.11325 405.77139999999997 L 386.12645 402.05435 L 383.4527 398.7963 L 380.19465 396.12255 L 376.47765 394.13575000000003 L 372.4444 392.9123 L 368.25 392.49915 L 364.0556 392.9123 L 360.02235 394.13575000000003 L 356.30535 396.12255 L 353.0473 398.7963 L 350.37355 402.05435 L 348.38675 405.77139999999997 L 347.16325 409.8046 L 346.75015 413.999 L 346.75015 473.2496 L 347.16325 477.44399999999996 L 348.38675 481.47720000000004 L 350.37355 485.19425 L 353.0473 488.45230000000004 L 356.30535 491.12604999999996 L 360.02235 493.11285 L 364.0556 494.3363 L 368.25 494.74945 Z M 431.75 494.74945 L 435.9444 494.3363 L 439.97765 493.11285 L 443.69465 491.12604999999996 L 446.9527 488.45230000000004 L 449.62645 485.19425 L 451.61325 481.47720000000004 L 452.83675 477.44399999999996 L 453.24985 473.2496 L 453.24985 413.999 L 452.83675 409.8046 L 451.61325 405.77139999999997 L 449.62645 402.05435 L 446.9527 398.7963 L 443.69465 396.12255 L 439.97765 394.13575000000003 L 435.9444 392.9123 L 431.75 392.49915 L 427.5556 392.9123 L 423.52235 394.13575000000003 L 419.80535 396.12255 L 416.5473 398.7963 L 413.87355 402.05435 L 411.88675 405.77139999999997 L 410.66325 409.8046 L 410.25015 413.999 L 410.25015 473.2496 L 410.66325 477.44399999999996 L 411.88675 481.47720000000004 L 413.87355 485.19425 L 416.5473 488.45230000000004 L 419.80535 491.12604999999996 L 423.52235 493.11285 L 427.5556 494.3363 L 431.75 494.74945 Z M 495.25 494.74945 L 499.4444 494.3363 L 503.47765 493.11285 L 507.19465 491.12604999999996 L 510.4527 488.45230000000004 L 513.12645 485.19425 L 515.11325 481.47720000000004 L 516.33675 477.44399999999996 L 516.74985 473.2496 L 516.74985 413.999 L 516.33675 409.8046 L 515.11325 405.77139999999997 L 513.12645 402.05435 L 510.4527 398.7963 L 507.19465 396.12255 L 503.47765 394.13575000000003 L 499.4444 392.9123 L 495.25 392.49915 L 491.0556 392.9123 L 487.02235 394.13575000000003 L 483.30535 396.12255 L 480.0473 398.7963 L 477.37355 402.05435 L 475.38675 405.77139999999997 L 474.16325 409.8046 L 473.75015 413.999 L 473.75015 473.2496 L 474.16325 477.44399999999996 L 475.38675 481.47720000000004 L 477.37355 485.19425 L 480.0473 488.45230000000004 L 483.30535 491.12604999999996 L 487.02235 493.11285 L 491.0556 494.3363 L 495.25 494.74945 Z M 558.75 494.74945 L 562.9444 494.3363 L 566.97765 493.11285 L 570.69465 491.12604999999996 L 573.9527 488.45230000000004 L 576.62645 485.19425 L 578.61325 481.47720000000004 L 579.8367499999999 477.44399999999996 L 580.24985 473.2496 L 580.24985 413.999 L 579.8367499999999 409.8046 L 578.61325 405.77139999999997 L 576.62645 402.05435 L 573.9527 398.7963 L 570.69465 396.12255 L 566.97765 394.13575000000003 L 562.9444 392.9123 L 558.75 392.49915 L 554.5556 392.9123 L 550.52235 394.13575000000003 L 546.80535 396.12255 L 543.5473 398.7963 L 540.87355 402.05435 L 538.88675 405.77139999999997 L 537.6632500000001 409.8046 L 537.25015 413.999 L 537.25015 473.2496 L 537.6632500000001 477.44399999999996 L 538.88675 481.47720000000004 L 540.87355 485.19425 L 543.5473 488.45230000000004 L 546.80535 491.12604999999996 L 550.52235 493.11285 L 554.5556 494.3363 L 558.75 494.74945 Z M 622.25 494.74945 L 626.4444 494.3363 L 630.47765 493.11285 L 634.19465 491.12604999999996 L 637.4527 488.45230000000004 L 640.12645 485.19425 L 642.11325 481.47720000000004 L 643.33675 477.44399999999996 L 643.7498499999999 473.2496 L 643.7498499999999 413.999 L 643.33675 409.8046 L 642.11325 405.77139999999997 L 640.12645 402.05435 L 637.4527 398.7963 L 634.19465 396.12255 L 630.47765 394.13575000000003 L 626.4444 392.9123 L 622.25 392.49915 L 618.0556 392.9123 L 614.02235 394.13575000000003 L 610.30535 396.12255 L 607.0473 398.7963 L 604.37355 402.05435 L 602.38675 405.77139999999997 L 601.1632500000001 409.8046 L 600.7501500000001 413.999 L 600.7501500000001 473.2496 L 601.1632500000001 477.44399999999996 L 602.38675 481.47720000000004 L 604.37355 485.19425 L 607.0473 488.45230000000004 L 610.30535 491.12604999999996 L 614.02235 493.11285 L 618.0556 494.3363 L 622.25 494.74945 Z" fill="rgb(200, 52, 52)" fill-rule="evenodd" fill-opacity="0.5" data-type="pcb_copper_pour" data-pcb-layer="top"/></svg>
@@ -0,0 +1,100 @@
1
+ import { expect, test } from "bun:test"
2
+ import type { AnyCircuitElement } from "circuit-json"
3
+ import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map"
4
+ import { convertCircuitJsonToInputProblem } from "lib/circuit-json/convert-circuit-json-to-input-problem"
5
+
6
+ const circuitJson = [
7
+ {
8
+ type: "pcb_board",
9
+ pcb_board_id: "pcb_board_0",
10
+ center: { x: 0, y: 0 },
11
+ width: 10,
12
+ height: 10,
13
+ thickness: 1.4,
14
+ num_layers: 2,
15
+ material: "fr4",
16
+ },
17
+ {
18
+ type: "source_net",
19
+ source_net_id: "source_net_gnd",
20
+ name: "GND",
21
+ subcircuit_connectivity_map_key: "stable_subcircuit_net_gnd",
22
+ },
23
+ {
24
+ type: "source_trace",
25
+ source_trace_id: "source_trace_gnd",
26
+ connected_source_net_ids: ["source_net_gnd"],
27
+ connected_source_port_ids: ["source_port_gnd"],
28
+ subcircuit_connectivity_map_key: "stable_subcircuit_net_gnd",
29
+ },
30
+ {
31
+ type: "source_port",
32
+ source_port_id: "source_port_gnd",
33
+ source_component_id: "source_component_r1",
34
+ name: "1",
35
+ subcircuit_connectivity_map_key: "stable_subcircuit_net_gnd",
36
+ },
37
+ {
38
+ type: "pcb_port",
39
+ pcb_port_id: "pcb_port_gnd",
40
+ pcb_component_id: "pcb_component_r1",
41
+ source_port_id: "source_port_gnd",
42
+ x: 0,
43
+ y: 0,
44
+ layers: ["top"],
45
+ },
46
+ {
47
+ type: "pcb_smtpad",
48
+ pcb_smtpad_id: "pcb_smtpad_gnd",
49
+ pcb_component_id: "pcb_component_r1",
50
+ pcb_port_id: "pcb_port_gnd",
51
+ layer: "top",
52
+ shape: "rect",
53
+ x: 0,
54
+ y: 0,
55
+ width: 1,
56
+ height: 1,
57
+ port_hints: ["1"],
58
+ },
59
+ ] as AnyCircuitElement[]
60
+
61
+ test("circuit-json adapter resolves pours to subcircuit connectivity keys", () => {
62
+ const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)
63
+ const generatedConnectivityMapKey =
64
+ connectivityMap.getNetConnectedToId("source_net_gnd")
65
+
66
+ expect(generatedConnectivityMapKey).toBeDefined()
67
+ expect(generatedConnectivityMapKey).not.toBe("stable_subcircuit_net_gnd")
68
+
69
+ const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
70
+ layer: "top",
71
+ source_net_id: "source_net_gnd",
72
+ pad_margin: 0.2,
73
+ trace_margin: 0.2,
74
+ })
75
+
76
+ expect(inputProblem.regionsForPour[0]?.connectivityKey).toBe(
77
+ "stable_subcircuit_net_gnd",
78
+ )
79
+ expect(
80
+ inputProblem.pads.find((pad) => pad.padId === "pcb_smtpad_gnd"),
81
+ ).toMatchObject({
82
+ connectivityKey: "stable_subcircuit_net_gnd",
83
+ })
84
+ })
85
+
86
+ test("circuit-json adapter rejects generated connectivity map keys", () => {
87
+ const generatedConnectivityMapKey =
88
+ getFullConnectivityMapFromCircuitJson(circuitJson).getNetConnectedToId(
89
+ "source_net_gnd",
90
+ )
91
+
92
+ expect(() =>
93
+ convertCircuitJsonToInputProblem(circuitJson, {
94
+ layer: "top",
95
+ pour_connectivity_key: generatedConnectivityMapKey!,
96
+ pad_margin: 0.2,
97
+ trace_margin: 0.2,
98
+ }),
99
+ ).toThrow(/subcircuit_connectivity_map_key/)
100
+ })