@tscircuit/copper-pour-solver 0.0.18 → 0.0.20

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/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 } from 'circuit-json';
3
+ import { BRepShape, AnyCircuitElement, LayerRef, Point as Point$1 } from 'circuit-json';
4
4
 
5
5
  interface InputPourRegion {
6
6
  shape: "rect";
@@ -61,6 +61,7 @@ declare const convertCircuitJsonToInputProblem: (circuitJson: AnyCircuitElement[
61
61
  trace_margin: number;
62
62
  board_edge_margin?: number;
63
63
  cutout_margin?: number;
64
+ outline?: Point$1[];
64
65
  }) => InputProblem;
65
66
 
66
67
  export { type BaseInputPad, CopperPourPipelineSolver, type InputCircularPad, type InputPad, type InputPolygonPad, type InputPourRegion, type InputProblem, type InputRectPad, type InputTracePad, type PipelineOutput, convertCircuitJsonToInputProblem };
package/dist/index.js CHANGED
@@ -237,7 +237,6 @@ var processObstaclesForPour = (pads, pourConnectivityKey, margins, boardOutline)
237
237
  )
238
238
  );
239
239
  }
240
- continue;
241
240
  }
242
241
  }
243
242
  return { polygonsToSubtract };
@@ -501,17 +500,31 @@ var convertCircuitJsonToInputProblem = (circuitJson, options) => {
501
500
  }
502
501
  }
503
502
  const { width, height } = pcb_board;
503
+ const outline = options.outline ?? pcb_board.outline;
504
+ let bounds;
505
+ if (outline && outline.length > 0) {
506
+ const xs = outline.map((p) => p.x);
507
+ const ys = outline.map((p) => p.y);
508
+ bounds = {
509
+ minX: Math.min(...xs),
510
+ minY: Math.min(...ys),
511
+ maxX: Math.max(...xs),
512
+ maxY: Math.max(...ys)
513
+ };
514
+ } else {
515
+ bounds = {
516
+ minX: -width / 2,
517
+ minY: -height / 2,
518
+ maxX: width / 2,
519
+ maxY: height / 2
520
+ };
521
+ }
504
522
  const regionsForPour = [
505
523
  {
506
524
  shape: "rect",
507
525
  layer: options.layer,
508
- bounds: {
509
- minX: -width / 2,
510
- minY: -height / 2,
511
- maxX: width / 2,
512
- maxY: height / 2
513
- },
514
- outline: pcb_board.outline,
526
+ bounds,
527
+ outline,
515
528
  connectivityKey: options.pour_connectivity_key,
516
529
  padMargin: options.pad_margin,
517
530
  traceMargin: options.trace_margin,
@@ -32,6 +32,7 @@ export const convertCircuitJsonToInputProblem = (
32
32
  trace_margin: number
33
33
  board_edge_margin?: number
34
34
  cutout_margin?: number
35
+ outline?: Point[]
35
36
  },
36
37
  ): InputProblem => {
37
38
  const pcb_board = circuitJson.find((e) => e.type === "pcb_board") as
@@ -207,17 +208,35 @@ export const convertCircuitJsonToInputProblem = (
207
208
  }
208
209
 
209
210
  const { width, height } = pcb_board
211
+
212
+ // Use pour-specific outline if provided, otherwise fall back to board outline
213
+ const outline = options.outline ?? pcb_board.outline
214
+
215
+ let bounds: { minX: number; minY: number; maxX: number; maxY: number }
216
+ if (outline && outline.length > 0) {
217
+ const xs = outline.map((p) => p.x)
218
+ const ys = outline.map((p) => p.y)
219
+ bounds = {
220
+ minX: Math.min(...xs),
221
+ minY: Math.min(...ys),
222
+ maxX: Math.max(...xs),
223
+ maxY: Math.max(...ys),
224
+ }
225
+ } else {
226
+ bounds = {
227
+ minX: -width! / 2,
228
+ minY: -height! / 2,
229
+ maxX: width! / 2,
230
+ maxY: height! / 2,
231
+ }
232
+ }
233
+
210
234
  const regionsForPour = [
211
235
  {
212
236
  shape: "rect" as const,
213
237
  layer: options.layer,
214
- bounds: {
215
- minX: -width! / 2,
216
- minY: -height! / 2,
217
- maxX: width! / 2,
218
- maxY: height! / 2,
219
- },
220
- outline: pcb_board.outline,
238
+ bounds,
239
+ outline,
221
240
  connectivityKey: options.pour_connectivity_key,
222
241
  padMargin: options.pad_margin,
223
242
  traceMargin: options.trace_margin,
@@ -271,7 +271,6 @@ export const processObstaclesForPour = (
271
271
  ),
272
272
  )
273
273
  }
274
- continue
275
274
  }
276
275
  }
277
276
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tscircuit/copper-pour-solver",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.18",
4
+ "version": "0.0.20",
5
5
  "scripts": {
6
6
  "format": "biome format . --write",
7
7
  "format:check": "biome format .",
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="800" height="600" data-software-used-string="@tscircuit/core@0.0.985"><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="150" y="50" width="500" height="500" data-type="pcb_boundary" data-pcb-layer="global"/><path class="pcb-board" d="M 150 550 L 650 550 L 650 50 L 150 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="463.75" y="276.25" width="40" height="47.5" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="546.25" y="276.25" width="40" height="47.5" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="213.75" y="276.25" width="40" height="47.5" data-type="pcb_smtpad" data-pcb-layer="top"/><rect class="pcb-pad" fill="rgb(200, 52, 52)" x="296.25" y="276.25" width="40" height="47.5" data-type="pcb_smtpad" data-pcb-layer="top"/><path class="pcb-trace" stroke="rgb(200, 52, 52)" fill="none" d="M 316.25 300 L 483.75 300" stroke-width="7.5" stroke-linecap="round" stroke-linejoin="round" shape-rendering="crispEdges" data-type="pcb_trace" data-pcb-layer="top"/><path class="pcb-trace" stroke="rgb(200, 52, 52)" fill="none" d="M 483.75 300 L 483.75 300" stroke-width="7.5" stroke-linecap="round" stroke-linejoin="round" shape-rendering="crispEdges" data-type="pcb_trace" data-pcb-layer="top"/><path class="pcb-copper-pour pcb-copper-pour-brep" d="M 382.75 313.75 L 400 400 L 250 500 L 200 550 L 150 550 L 150 450 L 150 150 L 350 150 L 377.25 286.25 L 346.25 286.25 L 346.25 266.25 L 286.25 266.25 L 286.25 333.75 L 346.25 333.75 L 346.25 313.75 L 382.75 313.75 Z" fill="rgb(200, 52, 52)" fill-rule="evenodd" fill-opacity="0.5" data-type="pcb_copper_pour" data-pcb-layer="top"/><path class="pcb-copper-pour pcb-copper-pour-brep" d="M 550 550 L 550 500 L 400 400 L 450 150 L 650 150 L 650 550 L 550 550 Z M 536.25 333.75 L 596.25 333.75 L 596.25 266.25 L 536.25 266.25 L 536.25 333.75 Z" fill="rgb(200, 52, 52)" fill-rule="evenodd" fill-opacity="0.5" data-type="pcb_copper_pour" data-pcb-layer="top"/><path class="pcb-silkscreen pcb-silkscreen-top" d="M 566.25 256.25 L 453.75 256.25 L 453.75 343.75 L 566.25 343.75" fill="none" stroke="#f2eda1" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" data-pcb-component-id="pcb_component_0" data-pcb-silkscreen-path-id="pcb_silkscreen_path_0" data-type="pcb_silkscreen_path" data-pcb-layer="top"/><text x="0" y="0" dx="0" dy="0" fill="#f2eda1" font-family="Arial, sans-serif" font-size="20" text-anchor="middle" dominant-baseline="central" transform="matrix(1,0,0,1,525,231.25)" class="pcb-silkscreen-text pcb-silkscreen-top" data-pcb-silkscreen-text-id="pcb_component_0" stroke="none" data-type="pcb_silkscreen_text" data-pcb-layer="top">R1</text><path class="pcb-silkscreen pcb-silkscreen-top" d="M 316.25 256.25 L 203.75 256.25 L 203.75 343.75 L 316.25 343.75" fill="none" stroke="#f2eda1" stroke-width="5" stroke-linecap="round" stroke-linejoin="round" data-pcb-component-id="pcb_component_1" data-pcb-silkscreen-path-id="pcb_silkscreen_path_1" data-type="pcb_silkscreen_path" data-pcb-layer="top"/><text x="0" y="0" dx="0" dy="0" fill="#f2eda1" font-family="Arial, sans-serif" font-size="20" text-anchor="middle" dominant-baseline="central" transform="matrix(1,0,0,1,275,231.25)" class="pcb-silkscreen-text pcb-silkscreen-top" data-pcb-silkscreen-text-id="pcb_component_1" stroke="none" data-type="pcb_silkscreen_text" data-pcb-layer="top">C1</text></svg>
@@ -0,0 +1,657 @@
1
+ [
2
+ {
3
+ "type": "source_project_metadata",
4
+ "source_project_metadata_id": "source_project_metadata_0",
5
+ "software_used_string": "@tscircuit/core@0.0.985"
6
+ },
7
+ {
8
+ "type": "source_group",
9
+ "source_group_id": "source_group_0",
10
+ "is_subcircuit": true,
11
+ "was_automatically_named": true,
12
+ "subcircuit_id": "subcircuit_source_group_0"
13
+ },
14
+ {
15
+ "type": "source_port",
16
+ "source_port_id": "source_port_0",
17
+ "name": "pin1",
18
+ "pin_number": 1,
19
+ "port_hints": ["pin1", "anode", "pos", "left", "1"],
20
+ "source_component_id": "source_component_0",
21
+ "subcircuit_id": "subcircuit_source_group_0",
22
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net0"
23
+ },
24
+ {
25
+ "type": "source_port",
26
+ "source_port_id": "source_port_1",
27
+ "name": "pin2",
28
+ "pin_number": 2,
29
+ "port_hints": ["pin2", "cathode", "neg", "right", "2"],
30
+ "source_component_id": "source_component_0",
31
+ "subcircuit_id": "subcircuit_source_group_0"
32
+ },
33
+ {
34
+ "type": "source_component",
35
+ "source_component_id": "source_component_0",
36
+ "ftype": "simple_resistor",
37
+ "name": "R1",
38
+ "supplier_part_numbers": {
39
+ "jlcpcb": ["C21190", "C2907002", "C2907113"]
40
+ },
41
+ "resistance": 1000,
42
+ "display_resistance": "1kΩ",
43
+ "are_pins_interchangeable": true,
44
+ "source_group_id": "source_group_0"
45
+ },
46
+ {
47
+ "type": "source_port",
48
+ "source_port_id": "source_port_2",
49
+ "name": "pin1",
50
+ "pin_number": 1,
51
+ "port_hints": ["pin1", "pos", "anode", "1"],
52
+ "source_component_id": "source_component_1",
53
+ "subcircuit_id": "subcircuit_source_group_0",
54
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net1"
55
+ },
56
+ {
57
+ "type": "source_port",
58
+ "source_port_id": "source_port_3",
59
+ "name": "pin2",
60
+ "pin_number": 2,
61
+ "port_hints": ["pin2", "neg", "cathode", "2"],
62
+ "source_component_id": "source_component_1",
63
+ "subcircuit_id": "subcircuit_source_group_0",
64
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net0"
65
+ },
66
+ {
67
+ "type": "source_component",
68
+ "source_component_id": "source_component_1",
69
+ "ftype": "simple_capacitor",
70
+ "name": "C1",
71
+ "supplier_part_numbers": {
72
+ "jlcpcb": ["C1588", "C100040", "C5375733"]
73
+ },
74
+ "capacitance": 1e-9,
75
+ "display_capacitance": "1000pF",
76
+ "are_pins_interchangeable": true,
77
+ "source_group_id": "source_group_0"
78
+ },
79
+ {
80
+ "type": "source_net",
81
+ "source_net_id": "source_net_0",
82
+ "name": "VCC",
83
+ "member_source_group_ids": [],
84
+ "is_ground": false,
85
+ "is_power": true,
86
+ "is_positive_voltage_source": true,
87
+ "subcircuit_id": "subcircuit_source_group_0",
88
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net0"
89
+ },
90
+ {
91
+ "type": "source_net",
92
+ "source_net_id": "source_net_1",
93
+ "name": "GND",
94
+ "member_source_group_ids": [],
95
+ "is_ground": true,
96
+ "is_power": false,
97
+ "is_positive_voltage_source": false,
98
+ "subcircuit_id": "subcircuit_source_group_0",
99
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net1"
100
+ },
101
+ {
102
+ "type": "source_board",
103
+ "source_board_id": "source_board_0",
104
+ "source_group_id": "source_group_0"
105
+ },
106
+ {
107
+ "type": "source_trace",
108
+ "source_trace_id": "source_trace_0",
109
+ "connected_source_port_ids": ["source_port_0"],
110
+ "connected_source_net_ids": ["source_net_0"],
111
+ "subcircuit_id": "subcircuit_source_group_0",
112
+ "display_name": ".R1 > .pin1 to net.VCC",
113
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net0"
114
+ },
115
+ {
116
+ "type": "source_trace",
117
+ "source_trace_id": "source_trace_1",
118
+ "connected_source_port_ids": ["source_port_2"],
119
+ "connected_source_net_ids": ["source_net_1"],
120
+ "subcircuit_id": "subcircuit_source_group_0",
121
+ "max_length": null,
122
+ "display_name": ".C1 > .pin1 to net.GND",
123
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net1"
124
+ },
125
+ {
126
+ "type": "source_trace",
127
+ "source_trace_id": "source_trace_2",
128
+ "connected_source_port_ids": ["source_port_3"],
129
+ "connected_source_net_ids": ["source_net_0"],
130
+ "subcircuit_id": "subcircuit_source_group_0",
131
+ "max_length": null,
132
+ "display_name": ".C1 > .pin2 to net.VCC",
133
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net0"
134
+ },
135
+ {
136
+ "type": "source_pin_missing_trace_warning",
137
+ "source_pin_missing_trace_warning_id": "source_pin_missing_trace_warning_0",
138
+ "message": "Port pin2 on R1 is missing a trace",
139
+ "source_component_id": "source_component_0",
140
+ "source_port_id": "source_port_1",
141
+ "subcircuit_id": "subcircuit_source_group_0",
142
+ "warning_type": "source_pin_missing_trace_warning"
143
+ },
144
+ {
145
+ "type": "schematic_component",
146
+ "schematic_component_id": "schematic_component_0",
147
+ "center": {
148
+ "x": 0,
149
+ "y": 0
150
+ },
151
+ "size": {
152
+ "width": 1.1,
153
+ "height": 0.388910699999999
154
+ },
155
+ "source_component_id": "source_component_0",
156
+ "is_box_with_pins": true,
157
+ "symbol_name": "boxresistor_right",
158
+ "symbol_display_value": "1kΩ",
159
+ "schematic_group_id": "schematic_group_0"
160
+ },
161
+ {
162
+ "type": "schematic_component",
163
+ "schematic_component_id": "schematic_component_1",
164
+ "center": {
165
+ "x": -2.31,
166
+ "y": 0
167
+ },
168
+ "size": {
169
+ "width": 1.1,
170
+ "height": 0.84
171
+ },
172
+ "source_component_id": "source_component_1",
173
+ "is_box_with_pins": true,
174
+ "symbol_name": "capacitor_right",
175
+ "symbol_display_value": "1000pF",
176
+ "schematic_group_id": "schematic_group_0"
177
+ },
178
+ {
179
+ "type": "schematic_group",
180
+ "schematic_group_id": "schematic_group_0",
181
+ "is_subcircuit": true,
182
+ "subcircuit_id": "subcircuit_source_group_0",
183
+ "name": "unnamed_board1",
184
+ "center": {
185
+ "x": 0,
186
+ "y": 0
187
+ },
188
+ "width": 0,
189
+ "height": 0,
190
+ "schematic_component_ids": [],
191
+ "source_group_id": "source_group_0"
192
+ },
193
+ {
194
+ "type": "schematic_port",
195
+ "schematic_port_id": "schematic_port_0",
196
+ "schematic_component_id": "schematic_component_0",
197
+ "center": {
198
+ "x": -0.55,
199
+ "y": 0
200
+ },
201
+ "source_port_id": "source_port_0",
202
+ "facing_direction": "left",
203
+ "distance_from_component_edge": 0.4,
204
+ "pin_number": 1,
205
+ "display_pin_label": "anode",
206
+ "is_connected": true
207
+ },
208
+ {
209
+ "type": "schematic_port",
210
+ "schematic_port_id": "schematic_port_1",
211
+ "schematic_component_id": "schematic_component_0",
212
+ "center": {
213
+ "x": 0.55,
214
+ "y": 0
215
+ },
216
+ "source_port_id": "source_port_1",
217
+ "facing_direction": "right",
218
+ "distance_from_component_edge": 0.4,
219
+ "pin_number": 2,
220
+ "display_pin_label": "cathode",
221
+ "is_connected": false
222
+ },
223
+ {
224
+ "type": "schematic_port",
225
+ "schematic_port_id": "schematic_port_2",
226
+ "schematic_component_id": "schematic_component_1",
227
+ "center": {
228
+ "x": -2.8600000000000003,
229
+ "y": 0
230
+ },
231
+ "source_port_id": "source_port_2",
232
+ "facing_direction": "left",
233
+ "distance_from_component_edge": 0.4,
234
+ "pin_number": 1,
235
+ "display_pin_label": "pos",
236
+ "is_connected": false
237
+ },
238
+ {
239
+ "type": "schematic_port",
240
+ "schematic_port_id": "schematic_port_3",
241
+ "schematic_component_id": "schematic_component_1",
242
+ "center": {
243
+ "x": -1.76,
244
+ "y": 0
245
+ },
246
+ "source_port_id": "source_port_3",
247
+ "facing_direction": "right",
248
+ "distance_from_component_edge": 0.4,
249
+ "pin_number": 2,
250
+ "display_pin_label": "neg",
251
+ "is_connected": true
252
+ },
253
+ {
254
+ "type": "schematic_trace",
255
+ "schematic_trace_id": "schematic_trace_0",
256
+ "source_trace_id": "solver_R1.1-C1.2",
257
+ "edges": [
258
+ {
259
+ "from": {
260
+ "x": -0.55,
261
+ "y": 0
262
+ },
263
+ "to": {
264
+ "x": -1.7599999999999998,
265
+ "y": 0
266
+ }
267
+ }
268
+ ],
269
+ "junctions": [],
270
+ "subcircuit_connectivity_map_key": "unnamedsubcircuit654_connectivity_net0"
271
+ },
272
+ {
273
+ "type": "schematic_net_label",
274
+ "schematic_net_label_id": "schematic_net_label_0",
275
+ "text": "VCC",
276
+ "anchor_position": {
277
+ "x": -1.1549999999999998,
278
+ "y": 0
279
+ },
280
+ "center": {
281
+ "x": -1.1549999999999998,
282
+ "y": 0.09
283
+ },
284
+ "anchor_side": "bottom",
285
+ "source_net_id": "source_net_0",
286
+ "symbol_name": "rail_up"
287
+ },
288
+ {
289
+ "type": "schematic_net_label",
290
+ "schematic_net_label_id": "schematic_net_label_1",
291
+ "text": "GND",
292
+ "anchor_position": {
293
+ "x": -2.8600000000000003,
294
+ "y": 0
295
+ },
296
+ "center": {
297
+ "x": -3.0100000000000002,
298
+ "y": 0
299
+ },
300
+ "anchor_side": "right",
301
+ "source_net_id": "source_net_1"
302
+ },
303
+ {
304
+ "type": "pcb_component",
305
+ "pcb_component_id": "pcb_component_0",
306
+ "center": {
307
+ "x": 2.5,
308
+ "y": 0
309
+ },
310
+ "width": 2.45,
311
+ "height": 0.95,
312
+ "layer": "top",
313
+ "rotation": 0,
314
+ "source_component_id": "source_component_0",
315
+ "subcircuit_id": "subcircuit_source_group_0",
316
+ "do_not_place": false,
317
+ "obstructs_within_bounds": true,
318
+ "position_mode": "relative_to_group_anchor",
319
+ "positioned_relative_to_pcb_board_id": "pcb_board_0",
320
+ "display_offset_x": 2.5
321
+ },
322
+ {
323
+ "type": "pcb_component",
324
+ "pcb_component_id": "pcb_component_1",
325
+ "center": {
326
+ "x": -2.5,
327
+ "y": 0
328
+ },
329
+ "width": 2.45,
330
+ "height": 0.95,
331
+ "layer": "top",
332
+ "rotation": 0,
333
+ "source_component_id": "source_component_1",
334
+ "subcircuit_id": "subcircuit_source_group_0",
335
+ "do_not_place": false,
336
+ "obstructs_within_bounds": true,
337
+ "position_mode": "relative_to_group_anchor",
338
+ "positioned_relative_to_pcb_board_id": "pcb_board_0",
339
+ "display_offset_x": -2.5
340
+ },
341
+ {
342
+ "type": "pcb_board",
343
+ "pcb_board_id": "pcb_board_0",
344
+ "source_board_id": "source_board_0",
345
+ "center": {
346
+ "x": 0,
347
+ "y": 0
348
+ },
349
+ "thickness": 1.4,
350
+ "num_layers": 2,
351
+ "width": 10,
352
+ "height": 10,
353
+ "material": "fr4"
354
+ },
355
+ {
356
+ "type": "pcb_smtpad",
357
+ "pcb_smtpad_id": "pcb_smtpad_0",
358
+ "pcb_component_id": "pcb_component_0",
359
+ "pcb_port_id": "pcb_port_0",
360
+ "layer": "top",
361
+ "shape": "rect",
362
+ "width": 0.8,
363
+ "height": 0.95,
364
+ "port_hints": ["1", "left"],
365
+ "is_covered_with_solder_mask": false,
366
+ "x": 1.675,
367
+ "y": 0,
368
+ "subcircuit_id": "subcircuit_source_group_0"
369
+ },
370
+ {
371
+ "type": "pcb_solder_paste",
372
+ "pcb_solder_paste_id": "pcb_solder_paste_0",
373
+ "layer": "top",
374
+ "shape": "rect",
375
+ "width": 0.5599999999999999,
376
+ "height": 0.6649999999999999,
377
+ "x": 1.675,
378
+ "y": 0,
379
+ "pcb_component_id": "pcb_component_0",
380
+ "pcb_smtpad_id": "pcb_smtpad_0",
381
+ "subcircuit_id": "subcircuit_source_group_0"
382
+ },
383
+ {
384
+ "type": "pcb_smtpad",
385
+ "pcb_smtpad_id": "pcb_smtpad_1",
386
+ "pcb_component_id": "pcb_component_0",
387
+ "pcb_port_id": "pcb_port_1",
388
+ "layer": "top",
389
+ "shape": "rect",
390
+ "width": 0.8,
391
+ "height": 0.95,
392
+ "port_hints": ["2", "right"],
393
+ "is_covered_with_solder_mask": false,
394
+ "x": 3.325,
395
+ "y": 0,
396
+ "subcircuit_id": "subcircuit_source_group_0"
397
+ },
398
+ {
399
+ "type": "pcb_solder_paste",
400
+ "pcb_solder_paste_id": "pcb_solder_paste_1",
401
+ "layer": "top",
402
+ "shape": "rect",
403
+ "width": 0.5599999999999999,
404
+ "height": 0.6649999999999999,
405
+ "x": 3.325,
406
+ "y": 0,
407
+ "pcb_component_id": "pcb_component_0",
408
+ "pcb_smtpad_id": "pcb_smtpad_1",
409
+ "subcircuit_id": "subcircuit_source_group_0"
410
+ },
411
+ {
412
+ "type": "pcb_silkscreen_path",
413
+ "pcb_silkscreen_path_id": "pcb_silkscreen_path_0",
414
+ "pcb_component_id": "pcb_component_0",
415
+ "layer": "top",
416
+ "route": [
417
+ {
418
+ "x": 3.325,
419
+ "y": 0.875
420
+ },
421
+ {
422
+ "x": 1.075,
423
+ "y": 0.875
424
+ },
425
+ {
426
+ "x": 1.075,
427
+ "y": -0.875
428
+ },
429
+ {
430
+ "x": 3.325,
431
+ "y": -0.875
432
+ }
433
+ ],
434
+ "stroke_width": 0.1,
435
+ "subcircuit_id": "subcircuit_source_group_0"
436
+ },
437
+ {
438
+ "type": "pcb_silkscreen_text",
439
+ "pcb_silkscreen_text_id": "pcb_silkscreen_text_0",
440
+ "anchor_alignment": "center",
441
+ "anchor_position": {
442
+ "x": 2.5,
443
+ "y": 1.375
444
+ },
445
+ "font": "tscircuit2024",
446
+ "font_size": 0.4,
447
+ "layer": "top",
448
+ "text": "R1",
449
+ "ccw_rotation": 0,
450
+ "pcb_component_id": "pcb_component_0",
451
+ "subcircuit_id": "subcircuit_source_group_0"
452
+ },
453
+ {
454
+ "type": "pcb_smtpad",
455
+ "pcb_smtpad_id": "pcb_smtpad_2",
456
+ "pcb_component_id": "pcb_component_1",
457
+ "pcb_port_id": "pcb_port_2",
458
+ "layer": "top",
459
+ "shape": "rect",
460
+ "width": 0.8,
461
+ "height": 0.95,
462
+ "port_hints": ["1", "left"],
463
+ "is_covered_with_solder_mask": false,
464
+ "x": -3.325,
465
+ "y": 0,
466
+ "subcircuit_id": "subcircuit_source_group_0"
467
+ },
468
+ {
469
+ "type": "pcb_solder_paste",
470
+ "pcb_solder_paste_id": "pcb_solder_paste_2",
471
+ "layer": "top",
472
+ "shape": "rect",
473
+ "width": 0.5599999999999999,
474
+ "height": 0.6649999999999999,
475
+ "x": -3.325,
476
+ "y": 0,
477
+ "pcb_component_id": "pcb_component_1",
478
+ "pcb_smtpad_id": "pcb_smtpad_2",
479
+ "subcircuit_id": "subcircuit_source_group_0"
480
+ },
481
+ {
482
+ "type": "pcb_smtpad",
483
+ "pcb_smtpad_id": "pcb_smtpad_3",
484
+ "pcb_component_id": "pcb_component_1",
485
+ "pcb_port_id": "pcb_port_3",
486
+ "layer": "top",
487
+ "shape": "rect",
488
+ "width": 0.8,
489
+ "height": 0.95,
490
+ "port_hints": ["2", "right"],
491
+ "is_covered_with_solder_mask": false,
492
+ "x": -1.675,
493
+ "y": 0,
494
+ "subcircuit_id": "subcircuit_source_group_0"
495
+ },
496
+ {
497
+ "type": "pcb_solder_paste",
498
+ "pcb_solder_paste_id": "pcb_solder_paste_3",
499
+ "layer": "top",
500
+ "shape": "rect",
501
+ "width": 0.5599999999999999,
502
+ "height": 0.6649999999999999,
503
+ "x": -1.675,
504
+ "y": 0,
505
+ "pcb_component_id": "pcb_component_1",
506
+ "pcb_smtpad_id": "pcb_smtpad_3",
507
+ "subcircuit_id": "subcircuit_source_group_0"
508
+ },
509
+ {
510
+ "type": "pcb_silkscreen_path",
511
+ "pcb_silkscreen_path_id": "pcb_silkscreen_path_1",
512
+ "pcb_component_id": "pcb_component_1",
513
+ "layer": "top",
514
+ "route": [
515
+ {
516
+ "x": -1.675,
517
+ "y": 0.875
518
+ },
519
+ {
520
+ "x": -3.925,
521
+ "y": 0.875
522
+ },
523
+ {
524
+ "x": -3.925,
525
+ "y": -0.875
526
+ },
527
+ {
528
+ "x": -1.675,
529
+ "y": -0.875
530
+ }
531
+ ],
532
+ "stroke_width": 0.1,
533
+ "subcircuit_id": "subcircuit_source_group_0"
534
+ },
535
+ {
536
+ "type": "pcb_silkscreen_text",
537
+ "pcb_silkscreen_text_id": "pcb_silkscreen_text_1",
538
+ "anchor_alignment": "center",
539
+ "anchor_position": {
540
+ "x": -2.5,
541
+ "y": 1.375
542
+ },
543
+ "font": "tscircuit2024",
544
+ "font_size": 0.4,
545
+ "layer": "top",
546
+ "text": "C1",
547
+ "ccw_rotation": 0,
548
+ "pcb_component_id": "pcb_component_1",
549
+ "subcircuit_id": "subcircuit_source_group_0"
550
+ },
551
+ {
552
+ "type": "pcb_port",
553
+ "pcb_port_id": "pcb_port_0",
554
+ "pcb_component_id": "pcb_component_0",
555
+ "layers": ["top"],
556
+ "subcircuit_id": "subcircuit_source_group_0",
557
+ "x": 1.675,
558
+ "y": 0,
559
+ "source_port_id": "source_port_0"
560
+ },
561
+ {
562
+ "type": "pcb_port",
563
+ "pcb_port_id": "pcb_port_1",
564
+ "pcb_component_id": "pcb_component_0",
565
+ "layers": ["top"],
566
+ "subcircuit_id": "subcircuit_source_group_0",
567
+ "x": 3.325,
568
+ "y": 0,
569
+ "source_port_id": "source_port_1"
570
+ },
571
+ {
572
+ "type": "pcb_port",
573
+ "pcb_port_id": "pcb_port_2",
574
+ "pcb_component_id": "pcb_component_1",
575
+ "layers": ["top"],
576
+ "subcircuit_id": "subcircuit_source_group_0",
577
+ "x": -3.325,
578
+ "y": 0,
579
+ "source_port_id": "source_port_2"
580
+ },
581
+ {
582
+ "type": "pcb_port",
583
+ "pcb_port_id": "pcb_port_3",
584
+ "pcb_component_id": "pcb_component_1",
585
+ "layers": ["top"],
586
+ "subcircuit_id": "subcircuit_source_group_0",
587
+ "x": -1.675,
588
+ "y": 0,
589
+ "source_port_id": "source_port_3"
590
+ },
591
+ {
592
+ "type": "cad_component",
593
+ "cad_component_id": "cad_component_0",
594
+ "position": {
595
+ "x": 2.5,
596
+ "y": 0,
597
+ "z": 0.7
598
+ },
599
+ "rotation": {
600
+ "x": 0,
601
+ "y": 0,
602
+ "z": 0
603
+ },
604
+ "pcb_component_id": "pcb_component_0",
605
+ "source_component_id": "source_component_0",
606
+ "footprinter_string": "0603"
607
+ },
608
+ {
609
+ "type": "cad_component",
610
+ "cad_component_id": "cad_component_1",
611
+ "position": {
612
+ "x": -2.5,
613
+ "y": 0,
614
+ "z": 0.7
615
+ },
616
+ "rotation": {
617
+ "x": 0,
618
+ "y": 0,
619
+ "z": 0
620
+ },
621
+ "pcb_component_id": "pcb_component_1",
622
+ "source_component_id": "source_component_1",
623
+ "footprinter_string": "0603"
624
+ },
625
+ {
626
+ "type": "pcb_trace",
627
+ "pcb_trace_id": "source_net_0_0",
628
+ "connection_name": "source_net_0",
629
+ "route": [
630
+ {
631
+ "route_type": "wire",
632
+ "x": -1.675,
633
+ "y": 0,
634
+ "width": 0.15,
635
+ "layer": "top",
636
+ "start_pcb_port_id": "pcb_port_3"
637
+ },
638
+ {
639
+ "route_type": "wire",
640
+ "x": 1.675,
641
+ "y": 0,
642
+ "width": 0.15,
643
+ "layer": "top"
644
+ },
645
+ {
646
+ "route_type": "wire",
647
+ "x": 1.675,
648
+ "y": 0,
649
+ "width": 0.15,
650
+ "layer": "top",
651
+ "end_pcb_port_id": "pcb_port_0"
652
+ }
653
+ ],
654
+ "subcircuit_id": "subcircuit_source_group_0",
655
+ "source_trace_id": "source_net_0"
656
+ }
657
+ ]
@@ -0,0 +1,42 @@
1
+ import { expect, test } from "bun:test"
2
+ import type { AnyCircuitElement } from "circuit-json"
3
+ import circuitJson from "./assets/multiple-pours.json"
4
+ import { runSolverAndRenderToSvg } from "./utils/run-solver-and-render-to-svg"
5
+
6
+ test("multiple copper pours with complex outlines", async () => {
7
+ const svg = runSolverAndRenderToSvg(circuitJson as AnyCircuitElement[], [
8
+ {
9
+ layer: "top",
10
+ net_name: "GND",
11
+ pad_margin: 0.2,
12
+ trace_margin: 0.2,
13
+ outline: [
14
+ { x: -5, y: -5 },
15
+ { x: -4, y: -5 },
16
+ { x: -3, y: -4 },
17
+ { x: 0, y: -2 },
18
+ { x: -1, y: 3 },
19
+ { x: -5, y: 3 },
20
+ { x: -5, y: -3 },
21
+ { x: -5, y: -5 },
22
+ ],
23
+ },
24
+ {
25
+ layer: "top",
26
+ net_name: "VCC",
27
+ pad_margin: 0.2,
28
+ trace_margin: 0.2,
29
+ outline: [
30
+ { x: 3, y: -5 },
31
+ { x: 5, y: -5 },
32
+ { x: 5, y: 3 },
33
+ { x: 1, y: 3 },
34
+ { x: 0, y: -2 },
35
+ { x: 3, y: -4 },
36
+ { x: 3, y: -5 },
37
+ ],
38
+ },
39
+ ])
40
+
41
+ await expect(svg).toMatchSvgSnapshot(import.meta.path, "multiple-pours")
42
+ })
@@ -10,67 +10,79 @@ import type {
10
10
  } from "circuit-json"
11
11
  import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map"
12
12
  import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
13
+ import type { Point } from "@tscircuit/math-utils"
14
+
15
+ interface PourOptions {
16
+ layer: LayerRef
17
+ net_name: string
18
+ pad_margin: number
19
+ trace_margin: number
20
+ board_edge_margin?: number
21
+ cutout_margin?: number
22
+ outline?: Point[]
23
+ }
13
24
 
14
25
  export const runSolverAndRenderToSvg = (
15
26
  circuitJson: AnyCircuitElement[],
16
- pour_options: {
17
- layer: LayerRef
18
- net_name: string
19
- pad_margin: number
20
- trace_margin: number
21
- board_edge_margin?: number
22
- cutout_margin?: number
23
- },
27
+ pour_options: PourOptions | PourOptions[],
24
28
  ) => {
25
- const source_net = circuitJson.find(
26
- (elm): elm is SourceNet =>
27
- elm.type === "source_net" && elm.name === pour_options.net_name,
28
- )
29
-
30
- if (!source_net) {
31
- throw new Error(`Net with name "${pour_options.net_name}" not found`)
32
- }
29
+ const pourOptionsArray = Array.isArray(pour_options)
30
+ ? pour_options
31
+ : [pour_options]
33
32
 
34
33
  const connectivityMap = getFullConnectivityMapFromCircuitJson(circuitJson)
35
- let pour_connectivity_key = connectivityMap.getNetConnectedToId(
36
- source_net.source_net_id,
37
- )
34
+ const allCopperPours: PcbCopperPourBRep[] = []
38
35
 
39
- if (!pour_connectivity_key && source_net.subcircuit_connectivity_map_key) {
40
- pour_connectivity_key = source_net.subcircuit_connectivity_map_key
41
- }
36
+ for (const options of pourOptionsArray) {
37
+ const source_net = circuitJson.find(
38
+ (elm): elm is SourceNet =>
39
+ elm.type === "source_net" && elm.name === options.net_name,
40
+ )
41
+
42
+ if (!source_net) {
43
+ throw new Error(`Net with name "${options.net_name}" not found`)
44
+ }
42
45
 
43
- if (!pour_connectivity_key) {
44
- throw new Error(
45
- `Net "${pour_options.net_name}" has no connectivity mapping`,
46
+ let pour_connectivity_key = connectivityMap.getNetConnectedToId(
47
+ source_net.source_net_id,
46
48
  )
47
- }
48
49
 
49
- const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
50
- ...pour_options,
51
- pour_connectivity_key,
52
- })
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`)
56
+ }
57
+
58
+ const inputProblem = convertCircuitJsonToInputProblem(circuitJson, {
59
+ ...options,
60
+ pour_connectivity_key,
61
+ })
53
62
 
54
- const solver = new CopperPourPipelineSolver(inputProblem)
55
- const output = solver.getOutput()
63
+ const solver = new CopperPourPipelineSolver(inputProblem)
64
+ const output = solver.getOutput()
56
65
 
57
- const pcb_copper_pours: PcbCopperPourBRep[] = output.brep_shapes.map(
58
- (brep_shape, i) =>
59
- ({
60
- type: "pcb_copper_pour",
61
- shape: "brep",
62
- pcb_copper_pour_id: `pcb_copper_pour_${i}`,
63
- layer: pour_options.layer,
64
- source_net_id: source_net.source_net_id,
65
- brep_shape: {
66
- outer_ring: brep_shape.outer_ring,
67
- inner_rings: brep_shape.inner_rings,
68
- },
69
- covered_with_solder_mask: true,
70
- }) as PcbCopperPourBRep,
71
- )
66
+ const pcb_copper_pours: PcbCopperPourBRep[] = output.brep_shapes.map(
67
+ (brep_shape, i) =>
68
+ ({
69
+ type: "pcb_copper_pour",
70
+ shape: "brep",
71
+ pcb_copper_pour_id: `pcb_copper_pour_${allCopperPours.length + i}`,
72
+ layer: options.layer,
73
+ source_net_id: source_net.source_net_id,
74
+ brep_shape: {
75
+ outer_ring: brep_shape.outer_ring,
76
+ inner_rings: brep_shape.inner_rings,
77
+ },
78
+ covered_with_solder_mask: true,
79
+ }) as PcbCopperPourBRep,
80
+ )
81
+
82
+ allCopperPours.push(...pcb_copper_pours)
83
+ }
72
84
 
73
- const finalCircuitJson = [...circuitJson, ...pcb_copper_pours]
85
+ const finalCircuitJson = [...circuitJson, ...allCopperPours]
74
86
 
75
87
  const svg = convertCircuitJsonToPcbSvg(finalCircuitJson as any)
76
88
  return svg