@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.
@@ -0,0 +1,223 @@
1
+ import { expect, test } from "bun:test"
2
+ import { Circuit } from "@tscircuit/core"
3
+ import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
4
+ import type {
5
+ AnyCircuitElement,
6
+ PcbCopperPourBRep,
7
+ SourceNet,
8
+ } from "circuit-json"
9
+ import { CopperPourPipelineSolver } from "lib/index"
10
+ import { convertCircuitJsonToInputProblem } from "lib/circuit-json/convert-circuit-json-to-input-problem"
11
+
12
+ const SubcircuitChild = () => (
13
+ <subcircuit
14
+ name="SubcircuitChild"
15
+ pcbX={2}
16
+ pcbY={0}
17
+ autorouter="sequential_trace"
18
+ >
19
+ <pinheader
20
+ name="J2"
21
+ pinCount={2}
22
+ footprint="pinrow2"
23
+ pcbX={0}
24
+ pcbY={0}
25
+ pinLabels={{ pin1: "B", pin2: "C" }}
26
+ showSilkscreenPinLabels
27
+ connections={{ C: "net.GND" }}
28
+ />
29
+ </subcircuit>
30
+ )
31
+
32
+ interface SubcircuitConnectivityReproProps {
33
+ includeParentToChildTrace: boolean
34
+ }
35
+
36
+ const SubcircuitConnectivityRepro = ({
37
+ includeParentToChildTrace,
38
+ }: SubcircuitConnectivityReproProps) => (
39
+ <board
40
+ name="SubcircuitParent"
41
+ width="10mm"
42
+ height="7mm"
43
+ autorouter="sequential_trace"
44
+ >
45
+ <pinheader
46
+ name="J1"
47
+ pinCount={1}
48
+ footprint="pinrow1"
49
+ pcbX={-3}
50
+ pcbY={0}
51
+ pinLabels={{ pin1: "A" }}
52
+ showSilkscreenPinLabels
53
+ connections={{ A: "net.GND" }}
54
+ />
55
+
56
+ <SubcircuitChild />
57
+
58
+ {includeParentToChildTrace && (
59
+ <trace from="J1.A" to=".SubcircuitChild > net.GND" />
60
+ )}
61
+
62
+ <copperpour
63
+ name="top_gnd_pour"
64
+ layer="top"
65
+ connectsTo="net.GND"
66
+ padMargin="0.25mm"
67
+ traceMargin="0.2mm"
68
+ />
69
+
70
+ <pcbnotetext
71
+ text={
72
+ includeParentToChildTrace
73
+ ? "Expected: copper pour connects to A and C, but clears around B."
74
+ : "Expected: copper pour connects to A, but clears around B and C."
75
+ }
76
+ pcbX={0}
77
+ pcbY={4.35}
78
+ fontSize="0.22mm"
79
+ anchorAlignment="center"
80
+ color="#ffffff"
81
+ />
82
+ <pcbnotetext
83
+ text={
84
+ includeParentToChildTrace
85
+ ? "A is parent net.GND; C is child net.GND reached through the parent-to-child net."
86
+ : "A is parent net.GND; C is child net.GND with no parent-to-child net."
87
+ }
88
+ pcbX={0}
89
+ pcbY={4.05}
90
+ fontSize="0.22mm"
91
+ anchorAlignment="center"
92
+ color="#ffffff"
93
+ />
94
+ <pcbnotetext
95
+ text="B is a separate child pin, so the pour should clear around it."
96
+ pcbX={0}
97
+ pcbY={3.75}
98
+ fontSize="0.22mm"
99
+ anchorAlignment="center"
100
+ color="#ffffff"
101
+ />
102
+ </board>
103
+ )
104
+
105
+ const renderCircuitJson = async (
106
+ props: SubcircuitConnectivityReproProps,
107
+ ): Promise<AnyCircuitElement[]> => {
108
+ const circuit = new Circuit()
109
+ const originalLog = console.log
110
+ const originalWarn = console.warn
111
+ console.log = () => {}
112
+ console.warn = () => {}
113
+ try {
114
+ circuit.add(<SubcircuitConnectivityRepro {...props} />)
115
+ await circuit.renderUntilSettled()
116
+ return circuit.getCircuitJson()
117
+ } finally {
118
+ console.log = originalLog
119
+ console.warn = originalWarn
120
+ }
121
+ }
122
+
123
+ const getPadCenterX = (pad: any) => {
124
+ if (typeof pad.x === "number") return pad.x
125
+ return (pad.bounds.minX + pad.bounds.maxX) / 2
126
+ }
127
+
128
+ const getPcbPadConnectivityKeys = (inputProblem: any) => {
129
+ const [aPad, bPad, cPad] = inputProblem.pads
130
+ .filter((pad: any) => pad.shape === "circle" || pad.shape === "rect")
131
+ .sort((a: any, b: any) => getPadCenterX(a) - getPadCenterX(b))
132
+
133
+ return {
134
+ a: aPad?.connectivityKey,
135
+ b: bPad?.connectivityKey,
136
+ c: cPad?.connectivityKey,
137
+ }
138
+ }
139
+
140
+ const runSubcircuitConnectivityCase = async ({
141
+ includeParentToChildTrace,
142
+ snapshotName,
143
+ expectedSourceNetConnectivityKeyCount,
144
+ expectChildGndConnectedToPour,
145
+ }: {
146
+ includeParentToChildTrace: boolean
147
+ snapshotName: string
148
+ expectedSourceNetConnectivityKeyCount: number
149
+ expectChildGndConnectedToPour: boolean
150
+ }) => {
151
+ const circuitJson = await renderCircuitJson({ includeParentToChildTrace })
152
+ const gndSourceNets = circuitJson.filter(
153
+ (element): element is SourceNet =>
154
+ element.type === "source_net" && element.name === "GND",
155
+ )
156
+
157
+ expect(gndSourceNets).toHaveLength(2)
158
+ expect(
159
+ new Set(gndSourceNets.map((net) => net.subcircuit_connectivity_map_key))
160
+ .size,
161
+ ).toBe(expectedSourceNetConnectivityKeyCount)
162
+ expect(new Set(gndSourceNets.map((net) => net.subcircuit_id)).size).toBe(2)
163
+
164
+ const parentGndSourceNet = gndSourceNets.find((net) =>
165
+ net.subcircuit_connectivity_map_key?.includes("SubcircuitParent"),
166
+ )
167
+ expect(parentGndSourceNet).toBeDefined()
168
+
169
+ const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
170
+ layer: "top",
171
+ source_net_id: parentGndSourceNet!.source_net_id,
172
+ pad_margin: 0.25,
173
+ trace_margin: 0.2,
174
+ })
175
+
176
+ const pourConnectivityKey = inputProblem.regionsForPour[0]?.connectivityKey
177
+ const padConnectivityKeys = getPcbPadConnectivityKeys(inputProblem)
178
+
179
+ expect(padConnectivityKeys.a).toBe(pourConnectivityKey)
180
+ expect(padConnectivityKeys.b).not.toBe(pourConnectivityKey)
181
+ if (expectChildGndConnectedToPour) {
182
+ expect(padConnectivityKeys.c).toBe(pourConnectivityKey)
183
+ } else {
184
+ expect(padConnectivityKeys.c).not.toBe(pourConnectivityKey)
185
+ }
186
+
187
+ const output = new CopperPourPipelineSolver(inputProblem).getOutput()
188
+ const copperPours: PcbCopperPourBRep[] = output.brep_shapes.map(
189
+ (brep_shape, i) => ({
190
+ type: "pcb_copper_pour",
191
+ shape: "brep",
192
+ pcb_copper_pour_id: `pcb_copper_pour_tsx_subcircuit_${i}`,
193
+ layer: "top",
194
+ source_net_id: parentGndSourceNet!.source_net_id,
195
+ brep_shape,
196
+ covered_with_solder_mask: true,
197
+ }),
198
+ )
199
+ const svg = convertCircuitJsonToPcbSvg([
200
+ ...circuitJson.filter((element) => element.type !== "pcb_copper_pour"),
201
+ ...copperPours,
202
+ ] as any)
203
+
204
+ await expect(svg).toMatchSvgSnapshot(import.meta.path, snapshotName)
205
+ }
206
+
207
+ test("tsx subcircuit connectivity 01 connects child net through parent-to-child trace", async () => {
208
+ await runSubcircuitConnectivityCase({
209
+ includeParentToChildTrace: true,
210
+ snapshotName: "tsx-subcircuit-connectivity01",
211
+ expectedSourceNetConnectivityKeyCount: 1,
212
+ expectChildGndConnectedToPour: true,
213
+ })
214
+ })
215
+
216
+ test("tsx subcircuit connectivity 02 keeps child net separate without parent-to-child trace", async () => {
217
+ await runSubcircuitConnectivityCase({
218
+ includeParentToChildTrace: false,
219
+ snapshotName: "tsx-subcircuit-connectivity02",
220
+ expectedSourceNetConnectivityKeyCount: 2,
221
+ expectChildGndConnectedToPour: false,
222
+ })
223
+ })
@@ -8,7 +8,6 @@ import type {
8
8
  PcbCopperPourBRep,
9
9
  SourceNet,
10
10
  } from "circuit-json"
11
- import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map"
12
11
  import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
13
12
  import type { Point } from "@tscircuit/math-utils"
14
13
 
@@ -30,7 +29,6 @@ export const runSolverAndRenderToSvg = (
30
29
  ? pour_options
31
30
  : [pour_options]
32
31
 
33
- const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)
34
32
  const allCopperPours: PcbCopperPourBRep[] = []
35
33
 
36
34
  for (const options of pourOptionsArray) {
@@ -43,21 +41,15 @@ export const runSolverAndRenderToSvg = (
43
41
  throw new Error(`Net with name "${options.net_name}" not found`)
44
42
  }
45
43
 
46
- let pour_connectivity_key = connectivityMap.getNetConnectedToId(
47
- source_net.source_net_id,
48
- )
49
-
50
- if (!pour_connectivity_key && source_net.subcircuit_connectivity_map_key) {
51
- pour_connectivity_key = source_net.subcircuit_connectivity_map_key
52
- }
53
-
54
- if (!pour_connectivity_key) {
55
- throw new Error(`Net "${options.net_name}" has no connectivity mapping`)
44
+ if (!source_net.subcircuit_connectivity_map_key) {
45
+ throw new Error(
46
+ `Net "${options.net_name}" has no subcircuit_connectivity_map_key`,
47
+ )
56
48
  }
57
49
 
58
50
  const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
59
51
  ...options,
60
- pour_connectivity_key,
52
+ source_net_id: source_net.source_net_id,
61
53
  })
62
54
 
63
55
  const solver = new CopperPourPipelineSolver(inputProblem)
package/vercel.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "buildCommand": "npm run build:site",
3
+ "outputDirectory": "cosmos-export",
4
+ "framework": null
5
+ }