@tscircuit/copper-pour-solver 0.0.33 → 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,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,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
+ })
@@ -71,7 +71,7 @@ const circuitJson: AnyCircuitElement[] = [
71
71
  const getInputProblem = () =>
72
72
  convertCircuitJsonToInputProblem(circuitJson, {
73
73
  layer: "top",
74
- pour_connectivity_key: "net:GND",
74
+ subcircuit_connectivity_map_key: "net:GND",
75
75
  pad_margin: 0.15,
76
76
  trace_margin: 0.15,
77
77
  })