@tscircuit/copper-pour-solver 0.0.19 → 0.0.21
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 +2 -1
- package/dist/index.js +51 -18
- package/lib/circuit-json/convert-circuit-json-to-input-problem.ts +55 -17
- package/lib/solvers/copper-pour/process-obstacles.ts +0 -1
- package/package.json +1 -1
- package/tests/__snapshots__/multiple-pours.snap.svg +1 -0
- package/tests/assets/multiple-pours.json +657 -0
- package/tests/multiple-pours.test.ts +42 -0
- package/tests/repro02-pinrow4-copper-pour/__snapshots__/repro02-pinrow4-copper-pour.snap.svg +1 -0
- package/tests/repro02-pinrow4-copper-pour/repro02-pinrow4-copper-pour.json +2344 -0
- package/tests/repro02-pinrow4-copper-pour/repro02-pinrow4-copper-pour.test.ts +18 -0
- package/tests/utils/run-solver-and-render-to-svg.ts +60 -48
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 };
|
|
@@ -294,6 +293,7 @@ var CopperPourPipelineSolver = class extends BasePipelineSolver {
|
|
|
294
293
|
super(input);
|
|
295
294
|
this.input = input;
|
|
296
295
|
}
|
|
296
|
+
input;
|
|
297
297
|
pipelineDef = [];
|
|
298
298
|
getSolverName() {
|
|
299
299
|
return "CopperPourPipelineSolver";
|
|
@@ -385,7 +385,6 @@ var convertCircuitJsonToInputProblem = (circuitJson, options) => {
|
|
|
385
385
|
}
|
|
386
386
|
} else if (elm.type === "pcb_plated_hole") {
|
|
387
387
|
const platedHole = elm;
|
|
388
|
-
if (platedHole.shape !== "circle") continue;
|
|
389
388
|
if (!platedHole.layers.includes(options.layer)) continue;
|
|
390
389
|
let connectivityKey = connectivityMap.getNetConnectedToId(
|
|
391
390
|
platedHole.pcb_plated_hole_id
|
|
@@ -393,15 +392,35 @@ var convertCircuitJsonToInputProblem = (circuitJson, options) => {
|
|
|
393
392
|
if (!connectivityKey) {
|
|
394
393
|
connectivityKey = `unconnected-plated-hole:${platedHole.pcb_plated_hole_id}`;
|
|
395
394
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
395
|
+
if (platedHole.shape === "circle") {
|
|
396
|
+
pads.push({
|
|
397
|
+
shape: "circle",
|
|
398
|
+
padId: platedHole.pcb_plated_hole_id,
|
|
399
|
+
layer: options.layer,
|
|
400
|
+
connectivityKey,
|
|
401
|
+
x: platedHole.x,
|
|
402
|
+
y: platedHole.y,
|
|
403
|
+
radius: platedHole.outer_diameter / 2
|
|
404
|
+
});
|
|
405
|
+
} else if (platedHole.shape === "circular_hole_with_rect_pad") {
|
|
406
|
+
const rectWidth = platedHole.rect_pad_width;
|
|
407
|
+
const rectHeight = platedHole.rect_pad_height;
|
|
408
|
+
if (typeof rectWidth !== "number" || typeof rectHeight !== "number") {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
pads.push({
|
|
412
|
+
shape: "rect",
|
|
413
|
+
padId: platedHole.pcb_plated_hole_id,
|
|
414
|
+
layer: options.layer,
|
|
415
|
+
connectivityKey,
|
|
416
|
+
bounds: {
|
|
417
|
+
minX: platedHole.x - rectWidth / 2,
|
|
418
|
+
minY: platedHole.y - rectHeight / 2,
|
|
419
|
+
maxX: platedHole.x + rectWidth / 2,
|
|
420
|
+
maxY: platedHole.y + rectHeight / 2
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
405
424
|
} else if (elm.type === "pcb_hole") {
|
|
406
425
|
const hole = elm;
|
|
407
426
|
if (hole.hole_shape !== "circle") continue;
|
|
@@ -501,17 +520,31 @@ var convertCircuitJsonToInputProblem = (circuitJson, options) => {
|
|
|
501
520
|
}
|
|
502
521
|
}
|
|
503
522
|
const { width, height } = pcb_board;
|
|
523
|
+
const outline = options.outline ?? pcb_board.outline;
|
|
524
|
+
let bounds;
|
|
525
|
+
if (outline && outline.length > 0) {
|
|
526
|
+
const xs = outline.map((p) => p.x);
|
|
527
|
+
const ys = outline.map((p) => p.y);
|
|
528
|
+
bounds = {
|
|
529
|
+
minX: Math.min(...xs),
|
|
530
|
+
minY: Math.min(...ys),
|
|
531
|
+
maxX: Math.max(...xs),
|
|
532
|
+
maxY: Math.max(...ys)
|
|
533
|
+
};
|
|
534
|
+
} else {
|
|
535
|
+
bounds = {
|
|
536
|
+
minX: -width / 2,
|
|
537
|
+
minY: -height / 2,
|
|
538
|
+
maxX: width / 2,
|
|
539
|
+
maxY: height / 2
|
|
540
|
+
};
|
|
541
|
+
}
|
|
504
542
|
const regionsForPour = [
|
|
505
543
|
{
|
|
506
544
|
shape: "rect",
|
|
507
545
|
layer: options.layer,
|
|
508
|
-
bounds
|
|
509
|
-
|
|
510
|
-
minY: -height / 2,
|
|
511
|
-
maxX: width / 2,
|
|
512
|
-
maxY: height / 2
|
|
513
|
-
},
|
|
514
|
-
outline: pcb_board.outline,
|
|
546
|
+
bounds,
|
|
547
|
+
outline,
|
|
515
548
|
connectivityKey: options.pour_connectivity_key,
|
|
516
549
|
padMargin: options.pad_margin,
|
|
517
550
|
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
|
|
@@ -83,7 +84,6 @@ export const convertCircuitJsonToInputProblem = (
|
|
|
83
84
|
}
|
|
84
85
|
} else if (elm.type === "pcb_plated_hole") {
|
|
85
86
|
const platedHole = elm as PcbPlatedHole
|
|
86
|
-
if (platedHole.shape !== "circle") continue
|
|
87
87
|
if (!platedHole.layers.includes(options.layer)) continue
|
|
88
88
|
|
|
89
89
|
let connectivityKey = connectivityMap.getNetConnectedToId(
|
|
@@ -93,15 +93,35 @@ export const convertCircuitJsonToInputProblem = (
|
|
|
93
93
|
connectivityKey = `unconnected-plated-hole:${platedHole.pcb_plated_hole_id}`
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
if (platedHole.shape === "circle") {
|
|
97
|
+
pads.push({
|
|
98
|
+
shape: "circle",
|
|
99
|
+
padId: platedHole.pcb_plated_hole_id,
|
|
100
|
+
layer: options.layer,
|
|
101
|
+
connectivityKey,
|
|
102
|
+
x: platedHole.x,
|
|
103
|
+
y: platedHole.y,
|
|
104
|
+
radius: platedHole.outer_diameter / 2,
|
|
105
|
+
} as InputCircularPad)
|
|
106
|
+
} else if (platedHole.shape === "circular_hole_with_rect_pad") {
|
|
107
|
+
const rectWidth = (platedHole as any).rect_pad_width
|
|
108
|
+
const rectHeight = (platedHole as any).rect_pad_height
|
|
109
|
+
if (typeof rectWidth !== "number" || typeof rectHeight !== "number") {
|
|
110
|
+
continue
|
|
111
|
+
}
|
|
112
|
+
pads.push({
|
|
113
|
+
shape: "rect",
|
|
114
|
+
padId: platedHole.pcb_plated_hole_id,
|
|
115
|
+
layer: options.layer,
|
|
116
|
+
connectivityKey,
|
|
117
|
+
bounds: {
|
|
118
|
+
minX: platedHole.x - rectWidth / 2,
|
|
119
|
+
minY: platedHole.y - rectHeight / 2,
|
|
120
|
+
maxX: platedHole.x + rectWidth / 2,
|
|
121
|
+
maxY: platedHole.y + rectHeight / 2,
|
|
122
|
+
},
|
|
123
|
+
} as InputRectPad)
|
|
124
|
+
}
|
|
105
125
|
} else if (elm.type === "pcb_hole") {
|
|
106
126
|
const hole = elm as PcbHole
|
|
107
127
|
if (hole.hole_shape !== "circle") continue
|
|
@@ -207,17 +227,35 @@ export const convertCircuitJsonToInputProblem = (
|
|
|
207
227
|
}
|
|
208
228
|
|
|
209
229
|
const { width, height } = pcb_board
|
|
230
|
+
|
|
231
|
+
// Use pour-specific outline if provided, otherwise fall back to board outline
|
|
232
|
+
const outline = options.outline ?? pcb_board.outline
|
|
233
|
+
|
|
234
|
+
let bounds: { minX: number; minY: number; maxX: number; maxY: number }
|
|
235
|
+
if (outline && outline.length > 0) {
|
|
236
|
+
const xs = outline.map((p) => p.x)
|
|
237
|
+
const ys = outline.map((p) => p.y)
|
|
238
|
+
bounds = {
|
|
239
|
+
minX: Math.min(...xs),
|
|
240
|
+
minY: Math.min(...ys),
|
|
241
|
+
maxX: Math.max(...xs),
|
|
242
|
+
maxY: Math.max(...ys),
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
bounds = {
|
|
246
|
+
minX: -width! / 2,
|
|
247
|
+
minY: -height! / 2,
|
|
248
|
+
maxX: width! / 2,
|
|
249
|
+
maxY: height! / 2,
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
210
253
|
const regionsForPour = [
|
|
211
254
|
{
|
|
212
255
|
shape: "rect" as const,
|
|
213
256
|
layer: options.layer,
|
|
214
|
-
bounds
|
|
215
|
-
|
|
216
|
-
minY: -height! / 2,
|
|
217
|
-
maxX: width! / 2,
|
|
218
|
-
maxY: height! / 2,
|
|
219
|
-
},
|
|
220
|
-
outline: pcb_board.outline,
|
|
257
|
+
bounds,
|
|
258
|
+
outline,
|
|
221
259
|
connectivityKey: options.pour_connectivity_key,
|
|
222
260
|
padMargin: options.pad_margin,
|
|
223
261
|
traceMargin: options.trace_margin,
|
package/package.json
CHANGED
|
@@ -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>
|