circuit-json-to-lbrn 0.0.6 → 0.0.8
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.js +636 -16
- package/lib/ConvertContext.ts +1 -0
- package/lib/element-handlers/addPcbBoard/index.ts +50 -0
- package/lib/element-handlers/addPlatedHole/addCirclePlatedHole.ts +30 -72
- package/lib/element-handlers/addPlatedHole/addCircularHoleWithRectPad.ts +44 -3
- package/lib/element-handlers/addPlatedHole/addHoleWithPolygonPad.ts +55 -4
- package/lib/element-handlers/addPlatedHole/addOvalPlatedHole.ts +49 -4
- package/lib/element-handlers/addPlatedHole/addPillHoleWithRectPad.ts +52 -4
- package/lib/element-handlers/addPlatedHole/addPillPlatedHole.ts +52 -0
- package/lib/element-handlers/addPlatedHole/addRotatedPillHoleWithRectPad.ts +57 -4
- package/lib/element-handlers/addPlatedHole/index.ts +4 -1
- package/lib/helpers/circleShape.ts +42 -0
- package/lib/helpers/ovalShape.ts +51 -0
- package/lib/helpers/pathPointUtils.ts +45 -0
- package/lib/helpers/pillShape.ts +99 -0
- package/lib/helpers/polygonShape.ts +38 -0
- package/lib/helpers/roundedRectShape.ts +121 -0
- package/lib/index.ts +17 -3
- package/package.json +1 -1
- package/tests/examples/__snapshots__/board-outline.snap.svg +8 -0
- package/tests/examples/__snapshots__/lga-interconnect.snap.svg +2 -2
- package/tests/examples/__snapshots__/single-trace.snap.svg +2 -2
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-circle.snap.svg +8 -0
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-circular-hole-with-rect-pad.snap.svg +8 -0
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-oval.snap.svg +8 -0
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-pill-with-rect-pad.snap.svg +8 -0
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-pill.snap.svg +8 -0
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-polygon.snap.svg +8 -0
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-rotated-pill-with-rect-pad.snap.svg +8 -0
- package/tests/examples/addPlatedHole/pcb-plated-hole-circle.test.ts +62 -0
- package/tests/examples/addPlatedHole/pcb-plated-hole-circular-hole-with-rect-pad.test.ts +54 -0
- package/tests/examples/addPlatedHole/pcb-plated-hole-oval.test.ts +101 -0
- package/tests/examples/addPlatedHole/pcb-plated-hole-pill-with-rect-pad.test.ts +54 -0
- package/tests/examples/addPlatedHole/pcb-plated-hole-pill.test.ts +101 -0
- package/tests/examples/addPlatedHole/pcb-plated-hole-polygon.test.ts +59 -0
- package/tests/examples/addPlatedHole/pcb-plated-hole-rotated-pill-with-rect-pad.test.ts +56 -0
- package/tests/examples/board-outline.test.ts +102 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,576 @@
|
|
|
1
1
|
// lib/index.ts
|
|
2
|
-
import { LightBurnProject, CutSetting, ShapePath as
|
|
2
|
+
import { LightBurnProject, CutSetting, ShapePath as ShapePath10 } from "lbrnts";
|
|
3
3
|
import { cju as cju2 } from "@tscircuit/circuit-json-util";
|
|
4
4
|
|
|
5
5
|
// lib/element-handlers/addPlatedHole/addCirclePlatedHole.ts
|
|
6
6
|
import { ShapePath } from "lbrnts";
|
|
7
7
|
|
|
8
|
+
// lib/helpers/circleShape.ts
|
|
9
|
+
var createCirclePath = (centerX, centerY, radius, segments = 64) => {
|
|
10
|
+
const verts = [];
|
|
11
|
+
const prims = [];
|
|
12
|
+
for (let i = 0; i < segments; i++) {
|
|
13
|
+
const angle = i / segments * Math.PI * 2;
|
|
14
|
+
const x = centerX + radius * Math.cos(angle);
|
|
15
|
+
const y = centerY + radius * Math.sin(angle);
|
|
16
|
+
verts.push({ x, y });
|
|
17
|
+
prims.push({ type: 0 });
|
|
18
|
+
}
|
|
19
|
+
if (verts.length > 0) {
|
|
20
|
+
const firstVert = verts[0];
|
|
21
|
+
if (firstVert) {
|
|
22
|
+
verts.push({ ...firstVert });
|
|
23
|
+
prims.push({ type: 0 });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { verts, prims };
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// lib/element-handlers/addPlatedHole/addCirclePlatedHole.ts
|
|
30
|
+
var addCirclePlatedHole = (platedHole, ctx) => {
|
|
31
|
+
const { project, copperCutSetting, throughBoardCutSetting, origin } = ctx;
|
|
32
|
+
const centerX = platedHole.x + origin.x;
|
|
33
|
+
const centerY = platedHole.y + origin.y;
|
|
34
|
+
if (platedHole.outer_diameter > 0) {
|
|
35
|
+
const outerRadius = platedHole.outer_diameter / 2;
|
|
36
|
+
const outer = createCirclePath(centerX, centerY, outerRadius);
|
|
37
|
+
project.children.push(
|
|
38
|
+
new ShapePath({
|
|
39
|
+
cutIndex: copperCutSetting.index,
|
|
40
|
+
verts: outer.verts,
|
|
41
|
+
prims: outer.prims,
|
|
42
|
+
isClosed: true
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
if (platedHole.hole_diameter > 0) {
|
|
47
|
+
const innerRadius = platedHole.hole_diameter / 2;
|
|
48
|
+
const inner = createCirclePath(centerX, centerY, innerRadius);
|
|
49
|
+
project.children.push(
|
|
50
|
+
new ShapePath({
|
|
51
|
+
cutIndex: throughBoardCutSetting.index,
|
|
52
|
+
verts: inner.verts,
|
|
53
|
+
prims: inner.prims,
|
|
54
|
+
isClosed: true
|
|
55
|
+
})
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// lib/element-handlers/addPlatedHole/addOvalPlatedHole.ts
|
|
61
|
+
import { ShapePath as ShapePath2 } from "lbrnts";
|
|
62
|
+
|
|
63
|
+
// lib/helpers/ovalShape.ts
|
|
64
|
+
var createOvalPath = (centerX, centerY, width, height, rotation = 0, segments = 64) => {
|
|
65
|
+
const verts = [];
|
|
66
|
+
const prims = [];
|
|
67
|
+
const radiusX = width / 2;
|
|
68
|
+
const radiusY = height / 2;
|
|
69
|
+
const cosTheta = Math.cos(rotation);
|
|
70
|
+
const sinTheta = Math.sin(rotation);
|
|
71
|
+
for (let i = 0; i < segments; i++) {
|
|
72
|
+
const angle = i / segments * Math.PI * 2;
|
|
73
|
+
const localX = radiusX * Math.cos(angle);
|
|
74
|
+
const localY = radiusY * Math.sin(angle);
|
|
75
|
+
const rotatedX = centerX + localX * cosTheta - localY * sinTheta;
|
|
76
|
+
const rotatedY = centerY + localX * sinTheta + localY * cosTheta;
|
|
77
|
+
verts.push({ x: rotatedX, y: rotatedY });
|
|
78
|
+
prims.push({ type: 0 });
|
|
79
|
+
}
|
|
80
|
+
if (verts.length > 0) {
|
|
81
|
+
const firstVert = verts[0];
|
|
82
|
+
if (firstVert) {
|
|
83
|
+
verts.push({ ...firstVert });
|
|
84
|
+
prims.push({ type: 0 });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { verts, prims };
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// lib/element-handlers/addPlatedHole/addOvalPlatedHole.ts
|
|
91
|
+
var addOvalPlatedHole = (platedHole, ctx) => {
|
|
92
|
+
const { project, copperCutSetting, throughBoardCutSetting, origin } = ctx;
|
|
93
|
+
if (platedHole.outer_width <= 0 || platedHole.outer_height <= 0) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const centerX = platedHole.x + origin.x;
|
|
97
|
+
const centerY = platedHole.y + origin.y;
|
|
98
|
+
const rotation = (platedHole.ccw_rotation ?? 0) * (Math.PI / 180);
|
|
99
|
+
if (platedHole.outer_width > 0 && platedHole.outer_height > 0) {
|
|
100
|
+
const outer = createOvalPath(
|
|
101
|
+
centerX,
|
|
102
|
+
centerY,
|
|
103
|
+
platedHole.outer_width,
|
|
104
|
+
platedHole.outer_height,
|
|
105
|
+
rotation
|
|
106
|
+
);
|
|
107
|
+
project.children.push(
|
|
108
|
+
new ShapePath2({
|
|
109
|
+
cutIndex: copperCutSetting.index,
|
|
110
|
+
verts: outer.verts,
|
|
111
|
+
prims: outer.prims,
|
|
112
|
+
isClosed: true
|
|
113
|
+
})
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
if (platedHole.hole_width > 0 && platedHole.hole_height > 0) {
|
|
117
|
+
const inner = createOvalPath(
|
|
118
|
+
centerX,
|
|
119
|
+
centerY,
|
|
120
|
+
platedHole.hole_width,
|
|
121
|
+
platedHole.hole_height,
|
|
122
|
+
rotation
|
|
123
|
+
);
|
|
124
|
+
project.children.push(
|
|
125
|
+
new ShapePath2({
|
|
126
|
+
cutIndex: throughBoardCutSetting.index,
|
|
127
|
+
verts: inner.verts,
|
|
128
|
+
prims: inner.prims,
|
|
129
|
+
isClosed: true
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// lib/element-handlers/addPlatedHole/addCircularHoleWithRectPad.ts
|
|
136
|
+
import { ShapePath as ShapePath3 } from "lbrnts";
|
|
137
|
+
|
|
138
|
+
// lib/helpers/roundedRectShape.ts
|
|
139
|
+
var createRoundedRectPath = (centerX, centerY, width, height, borderRadius = 0, segments = 4, rotation = 0) => {
|
|
140
|
+
const verts = [];
|
|
141
|
+
const prims = [];
|
|
142
|
+
const halfWidth = width / 2;
|
|
143
|
+
const halfHeight = height / 2;
|
|
144
|
+
const rotatePoint = (x, y) => {
|
|
145
|
+
const cos = Math.cos(rotation);
|
|
146
|
+
const sin = Math.sin(rotation);
|
|
147
|
+
return {
|
|
148
|
+
x: x * cos - y * sin,
|
|
149
|
+
y: x * sin + y * cos
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
const addPoint = (x, y) => {
|
|
153
|
+
const rotated = rotation ? rotatePoint(x, y) : { x, y };
|
|
154
|
+
verts.push({ x: centerX + rotated.x, y: centerY + rotated.y });
|
|
155
|
+
prims.push({ type: 0 });
|
|
156
|
+
};
|
|
157
|
+
addPoint(-halfWidth + borderRadius, -halfHeight);
|
|
158
|
+
addPoint(halfWidth - borderRadius, -halfHeight);
|
|
159
|
+
if (borderRadius > 0) {
|
|
160
|
+
for (let i = 0; i <= segments; i++) {
|
|
161
|
+
const angle = Math.PI * 1.5 + i * Math.PI * 0.5 / segments;
|
|
162
|
+
addPoint(
|
|
163
|
+
halfWidth - borderRadius + Math.cos(angle) * borderRadius,
|
|
164
|
+
-halfHeight + borderRadius + Math.sin(angle) * borderRadius
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
addPoint(halfWidth, -halfHeight);
|
|
169
|
+
}
|
|
170
|
+
addPoint(halfWidth, -halfHeight + borderRadius);
|
|
171
|
+
addPoint(halfWidth, halfHeight - borderRadius);
|
|
172
|
+
if (borderRadius > 0) {
|
|
173
|
+
for (let i = 0; i <= segments; i++) {
|
|
174
|
+
const angle = i * Math.PI * 0.5 / segments;
|
|
175
|
+
addPoint(
|
|
176
|
+
halfWidth - borderRadius + Math.cos(angle) * borderRadius,
|
|
177
|
+
halfHeight - borderRadius + Math.sin(angle) * borderRadius
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
} else {
|
|
181
|
+
addPoint(halfWidth, halfHeight);
|
|
182
|
+
}
|
|
183
|
+
addPoint(halfWidth - borderRadius, halfHeight);
|
|
184
|
+
addPoint(-halfWidth + borderRadius, halfHeight);
|
|
185
|
+
if (borderRadius > 0) {
|
|
186
|
+
for (let i = 0; i <= segments; i++) {
|
|
187
|
+
const angle = Math.PI * 0.5 + i * Math.PI * 0.5 / segments;
|
|
188
|
+
addPoint(
|
|
189
|
+
-halfWidth + borderRadius + Math.cos(angle) * borderRadius,
|
|
190
|
+
halfHeight - borderRadius + Math.sin(angle) * borderRadius
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
addPoint(-halfWidth, halfHeight);
|
|
195
|
+
}
|
|
196
|
+
addPoint(-halfWidth, halfHeight - borderRadius);
|
|
197
|
+
addPoint(-halfWidth, -halfHeight + borderRadius);
|
|
198
|
+
if (borderRadius > 0) {
|
|
199
|
+
for (let i = 0; i <= segments; i++) {
|
|
200
|
+
const angle = Math.PI + i * Math.PI * 0.5 / segments;
|
|
201
|
+
addPoint(
|
|
202
|
+
-halfWidth + borderRadius + Math.cos(angle) * borderRadius,
|
|
203
|
+
-halfHeight + borderRadius + Math.sin(angle) * borderRadius
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
addPoint(-halfWidth, -halfHeight);
|
|
208
|
+
}
|
|
209
|
+
if (verts.length > 0) {
|
|
210
|
+
const firstVert = verts[0];
|
|
211
|
+
if (firstVert) {
|
|
212
|
+
verts.push({ ...firstVert });
|
|
213
|
+
prims.push({ type: 0 });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return { verts, prims };
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// lib/element-handlers/addPlatedHole/addCircularHoleWithRectPad.ts
|
|
220
|
+
var addCircularHoleWithRectPad = (platedHole, ctx) => {
|
|
221
|
+
const { project, copperCutSetting, throughBoardCutSetting, origin } = ctx;
|
|
222
|
+
const centerX = platedHole.x + origin.x;
|
|
223
|
+
const centerY = platedHole.y + origin.y;
|
|
224
|
+
const holeRadius = platedHole.hole_diameter / 2;
|
|
225
|
+
const padWidth = platedHole.rect_pad_width;
|
|
226
|
+
const padHeight = platedHole.rect_pad_height;
|
|
227
|
+
const borderRadius = platedHole.rect_border_radius ?? 0;
|
|
228
|
+
const padPath = createRoundedRectPath(
|
|
229
|
+
centerX,
|
|
230
|
+
centerY,
|
|
231
|
+
padWidth,
|
|
232
|
+
padHeight,
|
|
233
|
+
borderRadius
|
|
234
|
+
);
|
|
235
|
+
project.children.push(
|
|
236
|
+
new ShapePath3({
|
|
237
|
+
cutIndex: copperCutSetting.index,
|
|
238
|
+
verts: padPath.verts,
|
|
239
|
+
prims: padPath.prims,
|
|
240
|
+
isClosed: true
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
if (holeRadius > 0) {
|
|
244
|
+
const holeCenterX = centerX + platedHole.hole_offset_x;
|
|
245
|
+
const holeCenterY = centerY + platedHole.hole_offset_y;
|
|
246
|
+
const holePath = createCirclePath(holeCenterX, holeCenterY, holeRadius, 32);
|
|
247
|
+
project.children.push(
|
|
248
|
+
new ShapePath3({
|
|
249
|
+
cutIndex: throughBoardCutSetting.index,
|
|
250
|
+
verts: holePath.verts,
|
|
251
|
+
prims: holePath.prims,
|
|
252
|
+
isClosed: true
|
|
253
|
+
})
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// lib/element-handlers/addPlatedHole/addPillHoleWithRectPad.ts
|
|
259
|
+
import { ShapePath as ShapePath4 } from "lbrnts";
|
|
260
|
+
|
|
261
|
+
// lib/helpers/pathPointUtils.ts
|
|
262
|
+
var createPointAdder = (verts, prims, options = {}) => {
|
|
263
|
+
const { rotation = 0, rotationCenter, translation } = options;
|
|
264
|
+
const cos = Math.cos(rotation);
|
|
265
|
+
const sin = Math.sin(rotation);
|
|
266
|
+
const effectiveCenter = rotationCenter ?? translation ?? { x: 0, y: 0 };
|
|
267
|
+
return (x, y) => {
|
|
268
|
+
let px = x;
|
|
269
|
+
let py = y;
|
|
270
|
+
if (rotation) {
|
|
271
|
+
const cx = effectiveCenter.x;
|
|
272
|
+
const cy = effectiveCenter.y;
|
|
273
|
+
const dx = px - cx;
|
|
274
|
+
const dy = py - cy;
|
|
275
|
+
px = cx + dx * cos - dy * sin;
|
|
276
|
+
py = cy + dx * sin + dy * cos;
|
|
277
|
+
}
|
|
278
|
+
const tx = translation?.x ?? 0;
|
|
279
|
+
const ty = translation?.y ?? 0;
|
|
280
|
+
verts.push({ x: px + tx, y: py + ty });
|
|
281
|
+
prims.push({ type: 0 });
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
// lib/helpers/pillShape.ts
|
|
286
|
+
var createPillPath = (centerX, centerY, width, height, rotation = 0, segments = 32) => {
|
|
287
|
+
const verts = [];
|
|
288
|
+
const prims = [];
|
|
289
|
+
const halfWidth = width / 2;
|
|
290
|
+
const halfHeight = height / 2;
|
|
291
|
+
const radius = Math.min(halfWidth, halfHeight);
|
|
292
|
+
const isVertical = height > width;
|
|
293
|
+
const addPoint = createPointAdder(verts, prims, {
|
|
294
|
+
rotation,
|
|
295
|
+
rotationCenter: { x: centerX, y: centerY }
|
|
296
|
+
});
|
|
297
|
+
if (isVertical) {
|
|
298
|
+
const capOffset = halfHeight - radius;
|
|
299
|
+
for (let i = 0; i <= segments; i++) {
|
|
300
|
+
const angle = Math.PI + i / segments * Math.PI;
|
|
301
|
+
const x = centerX + radius * Math.cos(angle);
|
|
302
|
+
const y = centerY - capOffset + radius * Math.sin(angle);
|
|
303
|
+
addPoint(x, y);
|
|
304
|
+
}
|
|
305
|
+
addPoint(centerX + radius, centerY + capOffset);
|
|
306
|
+
for (let i = 0; i <= segments; i++) {
|
|
307
|
+
const angle = i / segments * Math.PI;
|
|
308
|
+
const x = centerX + radius * Math.cos(angle);
|
|
309
|
+
const y = centerY + capOffset + radius * Math.sin(angle);
|
|
310
|
+
addPoint(x, y);
|
|
311
|
+
}
|
|
312
|
+
addPoint(centerX - radius, centerY - capOffset);
|
|
313
|
+
} else {
|
|
314
|
+
const capOffset = halfWidth - radius;
|
|
315
|
+
for (let i = 0; i <= segments; i++) {
|
|
316
|
+
const angle = -Math.PI / 2 + i / segments * Math.PI;
|
|
317
|
+
const x = centerX + capOffset + radius * Math.cos(angle);
|
|
318
|
+
const y = centerY + radius * Math.sin(angle);
|
|
319
|
+
addPoint(x, y);
|
|
320
|
+
}
|
|
321
|
+
addPoint(centerX - capOffset, centerY + radius);
|
|
322
|
+
for (let i = 0; i <= segments; i++) {
|
|
323
|
+
const angle = Math.PI / 2 + i / segments * Math.PI;
|
|
324
|
+
const x = centerX - capOffset + radius * Math.cos(angle);
|
|
325
|
+
const y = centerY + radius * Math.sin(angle);
|
|
326
|
+
addPoint(x, y);
|
|
327
|
+
}
|
|
328
|
+
addPoint(centerX + capOffset, centerY - radius);
|
|
329
|
+
}
|
|
330
|
+
if (verts.length > 0) {
|
|
331
|
+
const firstVert = verts[0];
|
|
332
|
+
if (firstVert) {
|
|
333
|
+
verts.push({ ...firstVert });
|
|
334
|
+
prims.push({ type: 0 });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return { verts, prims };
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// lib/element-handlers/addPlatedHole/addPillHoleWithRectPad.ts
|
|
341
|
+
var addPillHoleWithRectPad = (platedHole, ctx) => {
|
|
342
|
+
const { project, copperCutSetting, throughBoardCutSetting, origin } = ctx;
|
|
343
|
+
const centerX = platedHole.x + origin.x;
|
|
344
|
+
const centerY = platedHole.y + origin.y;
|
|
345
|
+
const padWidth = platedHole.rect_pad_width;
|
|
346
|
+
const padHeight = platedHole.rect_pad_height;
|
|
347
|
+
const borderRadius = platedHole.rect_border_radius ?? 0;
|
|
348
|
+
if (padWidth > 0 && padHeight > 0) {
|
|
349
|
+
const padPath = createRoundedRectPath(
|
|
350
|
+
centerX,
|
|
351
|
+
centerY,
|
|
352
|
+
padWidth,
|
|
353
|
+
padHeight,
|
|
354
|
+
borderRadius
|
|
355
|
+
);
|
|
356
|
+
project.children.push(
|
|
357
|
+
new ShapePath4({
|
|
358
|
+
cutIndex: copperCutSetting.index,
|
|
359
|
+
verts: padPath.verts,
|
|
360
|
+
prims: padPath.prims,
|
|
361
|
+
isClosed: true
|
|
362
|
+
})
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
const holeWidth = platedHole.hole_width;
|
|
366
|
+
const holeHeight = platedHole.hole_height;
|
|
367
|
+
if (holeWidth > 0 && holeHeight > 0) {
|
|
368
|
+
const holeCenterX = centerX + platedHole.hole_offset_x;
|
|
369
|
+
const holeCenterY = centerY + platedHole.hole_offset_y;
|
|
370
|
+
const holePath = createPillPath(
|
|
371
|
+
holeCenterX,
|
|
372
|
+
holeCenterY,
|
|
373
|
+
holeWidth,
|
|
374
|
+
holeHeight
|
|
375
|
+
);
|
|
376
|
+
project.children.push(
|
|
377
|
+
new ShapePath4({
|
|
378
|
+
cutIndex: throughBoardCutSetting.index,
|
|
379
|
+
verts: holePath.verts,
|
|
380
|
+
prims: holePath.prims,
|
|
381
|
+
isClosed: true
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// lib/element-handlers/addPlatedHole/addRotatedPillHoleWithRectPad.ts
|
|
388
|
+
import { ShapePath as ShapePath5 } from "lbrnts";
|
|
389
|
+
var addRotatedPillHoleWithRectPad = (platedHole, ctx) => {
|
|
390
|
+
const { project, copperCutSetting, throughBoardCutSetting, origin } = ctx;
|
|
391
|
+
const centerX = platedHole.x + origin.x;
|
|
392
|
+
const centerY = platedHole.y + origin.y;
|
|
393
|
+
const padWidth = platedHole.rect_pad_width;
|
|
394
|
+
const padHeight = platedHole.rect_pad_height;
|
|
395
|
+
const borderRadius = platedHole.rect_border_radius ?? 0;
|
|
396
|
+
const padRotation = (platedHole.rect_ccw_rotation ?? 0) * (Math.PI / 180);
|
|
397
|
+
if (padWidth > 0 && padHeight > 0) {
|
|
398
|
+
const padPath = createRoundedRectPath(
|
|
399
|
+
centerX,
|
|
400
|
+
centerY,
|
|
401
|
+
padWidth,
|
|
402
|
+
padHeight,
|
|
403
|
+
borderRadius,
|
|
404
|
+
4,
|
|
405
|
+
padRotation
|
|
406
|
+
);
|
|
407
|
+
project.children.push(
|
|
408
|
+
new ShapePath5({
|
|
409
|
+
cutIndex: copperCutSetting.index,
|
|
410
|
+
verts: padPath.verts,
|
|
411
|
+
prims: padPath.prims,
|
|
412
|
+
isClosed: true
|
|
413
|
+
})
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
const holeWidth = platedHole.hole_width;
|
|
417
|
+
const holeHeight = platedHole.hole_height;
|
|
418
|
+
const holeRotation = (platedHole.hole_ccw_rotation ?? 0) * (Math.PI / 180);
|
|
419
|
+
if (holeWidth > 0 && holeHeight > 0) {
|
|
420
|
+
const holeCenterX = centerX + platedHole.hole_offset_x;
|
|
421
|
+
const holeCenterY = centerY + platedHole.hole_offset_y;
|
|
422
|
+
const holePath = createPillPath(
|
|
423
|
+
holeCenterX,
|
|
424
|
+
holeCenterY,
|
|
425
|
+
holeWidth,
|
|
426
|
+
holeHeight,
|
|
427
|
+
holeRotation
|
|
428
|
+
);
|
|
429
|
+
project.children.push(
|
|
430
|
+
new ShapePath5({
|
|
431
|
+
cutIndex: throughBoardCutSetting.index,
|
|
432
|
+
verts: holePath.verts,
|
|
433
|
+
prims: holePath.prims,
|
|
434
|
+
isClosed: true
|
|
435
|
+
})
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
// lib/element-handlers/addPlatedHole/addHoleWithPolygonPad.ts
|
|
441
|
+
import { ShapePath as ShapePath6 } from "lbrnts";
|
|
442
|
+
|
|
443
|
+
// lib/helpers/polygonShape.ts
|
|
444
|
+
var createPolygonPathFromOutline = (outline, offsetX, offsetY) => {
|
|
445
|
+
const verts = [];
|
|
446
|
+
for (const point2 of outline) {
|
|
447
|
+
const x = (point2.x ?? 0) + offsetX;
|
|
448
|
+
const y = (point2.y ?? 0) + offsetY;
|
|
449
|
+
verts.push({ x, y });
|
|
450
|
+
}
|
|
451
|
+
if (verts.length === 0) {
|
|
452
|
+
return { verts, prims: [] };
|
|
453
|
+
}
|
|
454
|
+
const first = verts[0];
|
|
455
|
+
verts.push({ x: first.x, y: first.y });
|
|
456
|
+
const prims = new Array(verts.length).fill({ type: 0 });
|
|
457
|
+
return { verts, prims };
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// lib/element-handlers/addPlatedHole/addHoleWithPolygonPad.ts
|
|
461
|
+
var addHoleWithPolygonPad = (platedHole, ctx) => {
|
|
462
|
+
const { project, copperCutSetting, throughBoardCutSetting, origin } = ctx;
|
|
463
|
+
if (platedHole.pad_outline.length >= 3) {
|
|
464
|
+
const pad = createPolygonPathFromOutline(
|
|
465
|
+
platedHole.pad_outline,
|
|
466
|
+
platedHole.x + origin.x,
|
|
467
|
+
platedHole.y + origin.y
|
|
468
|
+
);
|
|
469
|
+
project.children.push(
|
|
470
|
+
new ShapePath6({
|
|
471
|
+
cutIndex: copperCutSetting.index,
|
|
472
|
+
verts: pad.verts,
|
|
473
|
+
prims: pad.prims,
|
|
474
|
+
isClosed: true
|
|
475
|
+
})
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
if (platedHole.hole_shape === "circle" && platedHole.hole_diameter) {
|
|
479
|
+
const centerX = platedHole.x + platedHole.hole_offset_x + origin.x;
|
|
480
|
+
const centerY = platedHole.y + platedHole.hole_offset_y + origin.y;
|
|
481
|
+
const radius = platedHole.hole_diameter / 2;
|
|
482
|
+
const hole = createCirclePath(centerX, centerY, radius, 64);
|
|
483
|
+
project.children.push(
|
|
484
|
+
new ShapePath6({
|
|
485
|
+
cutIndex: throughBoardCutSetting.index,
|
|
486
|
+
verts: hole.verts,
|
|
487
|
+
prims: hole.prims,
|
|
488
|
+
isClosed: true
|
|
489
|
+
})
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
if (platedHole.hole_shape === "pill" && platedHole.hole_diameter) {
|
|
493
|
+
const centerX = platedHole.x + platedHole.hole_offset_x + origin.x;
|
|
494
|
+
const centerY = platedHole.y + platedHole.hole_offset_y + origin.y;
|
|
495
|
+
const radius = platedHole.hole_diameter / 2;
|
|
496
|
+
const hole = createCirclePath(centerX, centerY, radius, 64);
|
|
497
|
+
project.children.push(
|
|
498
|
+
new ShapePath6({
|
|
499
|
+
cutIndex: throughBoardCutSetting.index,
|
|
500
|
+
verts: hole.verts,
|
|
501
|
+
prims: hole.prims,
|
|
502
|
+
isClosed: true
|
|
503
|
+
})
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// lib/element-handlers/addPlatedHole/addPillPlatedHole.ts
|
|
509
|
+
import { ShapePath as ShapePath7 } from "lbrnts";
|
|
510
|
+
var addPcbPlatedHolePill = (platedHole, ctx) => {
|
|
511
|
+
const { project, copperCutSetting, throughBoardCutSetting, origin } = ctx;
|
|
512
|
+
const centerX = platedHole.x + origin.x;
|
|
513
|
+
const centerY = platedHole.y + origin.y;
|
|
514
|
+
const rotation = (platedHole.ccw_rotation || 0) * (Math.PI / 180);
|
|
515
|
+
if (platedHole.outer_width > 0 && platedHole.outer_height > 0) {
|
|
516
|
+
const outer = createPillPath(
|
|
517
|
+
centerX,
|
|
518
|
+
centerY,
|
|
519
|
+
platedHole.outer_width,
|
|
520
|
+
platedHole.outer_height,
|
|
521
|
+
rotation
|
|
522
|
+
);
|
|
523
|
+
project.children.push(
|
|
524
|
+
new ShapePath7({
|
|
525
|
+
cutIndex: copperCutSetting.index,
|
|
526
|
+
verts: outer.verts,
|
|
527
|
+
prims: outer.prims,
|
|
528
|
+
isClosed: true
|
|
529
|
+
})
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
if (platedHole.hole_width > 0 && platedHole.hole_height > 0) {
|
|
533
|
+
const inner = createPillPath(
|
|
534
|
+
centerX,
|
|
535
|
+
centerY,
|
|
536
|
+
platedHole.hole_width,
|
|
537
|
+
platedHole.hole_height,
|
|
538
|
+
rotation
|
|
539
|
+
);
|
|
540
|
+
project.children.push(
|
|
541
|
+
new ShapePath7({
|
|
542
|
+
cutIndex: throughBoardCutSetting.index,
|
|
543
|
+
verts: inner.verts,
|
|
544
|
+
prims: inner.prims,
|
|
545
|
+
isClosed: true
|
|
546
|
+
})
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
// lib/element-handlers/addPlatedHole/index.ts
|
|
552
|
+
var addPlatedHole = (platedHole, ctx) => {
|
|
553
|
+
switch (platedHole.shape) {
|
|
554
|
+
case "circle":
|
|
555
|
+
return addCirclePlatedHole(platedHole, ctx);
|
|
556
|
+
case "oval":
|
|
557
|
+
return addOvalPlatedHole(platedHole, ctx);
|
|
558
|
+
case "pill":
|
|
559
|
+
return addPcbPlatedHolePill(platedHole, ctx);
|
|
560
|
+
case "circular_hole_with_rect_pad":
|
|
561
|
+
return addCircularHoleWithRectPad(platedHole, ctx);
|
|
562
|
+
case "pill_hole_with_rect_pad":
|
|
563
|
+
return addPillHoleWithRectPad(platedHole, ctx);
|
|
564
|
+
case "rotated_pill_hole_with_rect_pad":
|
|
565
|
+
return addRotatedPillHoleWithRectPad(platedHole, ctx);
|
|
566
|
+
case "hole_with_polygon_pad":
|
|
567
|
+
return addHoleWithPolygonPad(platedHole, ctx);
|
|
568
|
+
default:
|
|
569
|
+
const _exhaustive = platedHole;
|
|
570
|
+
console.warn(`Unknown plated hole shape: ${platedHole.shape}`);
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
|
|
8
574
|
// lib/element-handlers/addSmtPad/addRectSmtPad.ts
|
|
9
575
|
import { Box } from "@flatten-js/core";
|
|
10
576
|
import "lbrnts";
|
|
@@ -69,7 +635,7 @@ var addPcbTrace = (trace, ctx) => {
|
|
|
69
635
|
return;
|
|
70
636
|
}
|
|
71
637
|
const { route } = trace;
|
|
72
|
-
const traceWidth = route.find((
|
|
638
|
+
const traceWidth = route.find((point2) => point2.route_type === "wire")?.width ?? 0.15;
|
|
73
639
|
const polygons = [];
|
|
74
640
|
for (const routePoint of route) {
|
|
75
641
|
const circle = new Flatten2.Circle(
|
|
@@ -117,9 +683,9 @@ var addPcbTrace = (trace, ctx) => {
|
|
|
117
683
|
netGeoms.get(netId)?.push(tracePolygon);
|
|
118
684
|
};
|
|
119
685
|
|
|
120
|
-
// lib/index.ts
|
|
121
|
-
import {
|
|
122
|
-
import {
|
|
686
|
+
// lib/element-handlers/addPcbBoard/index.ts
|
|
687
|
+
import { Polygon, point } from "@flatten-js/core";
|
|
688
|
+
import { ShapePath as ShapePath9 } from "lbrnts";
|
|
123
689
|
|
|
124
690
|
// lib/polygon-to-shape-path.ts
|
|
125
691
|
function polygonToShapePathData(polygon) {
|
|
@@ -148,6 +714,46 @@ function polygonToShapePathData(polygon) {
|
|
|
148
714
|
return { verts, prims };
|
|
149
715
|
}
|
|
150
716
|
|
|
717
|
+
// lib/element-handlers/addPcbBoard/index.ts
|
|
718
|
+
var addPcbBoard = (board, ctx) => {
|
|
719
|
+
const { origin, project, throughBoardCutSetting } = ctx;
|
|
720
|
+
let polygon = null;
|
|
721
|
+
if (board.outline?.length) {
|
|
722
|
+
polygon = new Polygon(
|
|
723
|
+
board.outline.map(
|
|
724
|
+
(outlinePoint) => point(outlinePoint.x + origin.x, outlinePoint.y + origin.y)
|
|
725
|
+
)
|
|
726
|
+
);
|
|
727
|
+
} else if (typeof board.width === "number" && typeof board.height === "number" && board.center) {
|
|
728
|
+
const halfWidth = board.width / 2;
|
|
729
|
+
const halfHeight = board.height / 2;
|
|
730
|
+
const minX = board.center.x - halfWidth + origin.x;
|
|
731
|
+
const minY = board.center.y - halfHeight + origin.y;
|
|
732
|
+
const maxX = board.center.x + halfWidth + origin.x;
|
|
733
|
+
const maxY = board.center.y + halfHeight + origin.y;
|
|
734
|
+
polygon = new Polygon([
|
|
735
|
+
point(minX, minY),
|
|
736
|
+
point(maxX, minY),
|
|
737
|
+
point(maxX, maxY),
|
|
738
|
+
point(minX, maxY)
|
|
739
|
+
]);
|
|
740
|
+
}
|
|
741
|
+
if (!polygon) return;
|
|
742
|
+
const { verts, prims } = polygonToShapePathData(polygon);
|
|
743
|
+
project.children.push(
|
|
744
|
+
new ShapePath9({
|
|
745
|
+
cutIndex: throughBoardCutSetting.index,
|
|
746
|
+
verts,
|
|
747
|
+
prims,
|
|
748
|
+
isClosed: true
|
|
749
|
+
})
|
|
750
|
+
);
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
// lib/index.ts
|
|
754
|
+
import { getFullConnectivityMapFromCircuitJson } from "circuit-json-to-connectivity-map";
|
|
755
|
+
import { Polygon as Polygon2, Box as Box2, BooleanOperations as BooleanOperations2 } from "@flatten-js/core";
|
|
756
|
+
|
|
151
757
|
// lib/calculateBounds.ts
|
|
152
758
|
import { cju } from "@tscircuit/circuit-json-util";
|
|
153
759
|
var calculateCircuitBounds = (circuitJson) => {
|
|
@@ -173,14 +779,14 @@ var calculateCircuitBounds = (circuitJson) => {
|
|
|
173
779
|
}
|
|
174
780
|
}
|
|
175
781
|
for (const trace of db.pcb_trace.list()) {
|
|
176
|
-
const isWidthPoint = (
|
|
782
|
+
const isWidthPoint = (point2) => "width" in point2 && typeof point2.width === "number";
|
|
177
783
|
const halfWidth = trace.route_thickness_mode === "interpolated" ? 0 : (trace.route.find(isWidthPoint)?.width ?? 0) / 2;
|
|
178
|
-
for (const
|
|
179
|
-
const pointWidth = trace.route_thickness_mode === "interpolated" ? isWidthPoint(
|
|
180
|
-
minX = Math.min(minX,
|
|
181
|
-
minY = Math.min(minY,
|
|
182
|
-
maxX = Math.max(maxX,
|
|
183
|
-
maxY = Math.max(maxY,
|
|
784
|
+
for (const point2 of trace.route) {
|
|
785
|
+
const pointWidth = trace.route_thickness_mode === "interpolated" ? isWidthPoint(point2) ? point2.width / 2 : 0 : halfWidth;
|
|
786
|
+
minX = Math.min(minX, point2.x - pointWidth);
|
|
787
|
+
minY = Math.min(minY, point2.y - pointWidth);
|
|
788
|
+
maxX = Math.max(maxX, point2.x + pointWidth);
|
|
789
|
+
maxY = Math.max(maxY, point2.y + pointWidth);
|
|
184
790
|
}
|
|
185
791
|
}
|
|
186
792
|
for (const hole of db.pcb_plated_hole.list()) {
|
|
@@ -218,6 +824,13 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
|
218
824
|
speed: 100
|
|
219
825
|
});
|
|
220
826
|
project.children.push(copperCutSetting);
|
|
827
|
+
const throughBoardCutSetting = new CutSetting({
|
|
828
|
+
index: 1,
|
|
829
|
+
name: "Cut Through Board",
|
|
830
|
+
numPasses: 3,
|
|
831
|
+
speed: 50
|
|
832
|
+
});
|
|
833
|
+
project.children.push(throughBoardCutSetting);
|
|
221
834
|
const connMap = getFullConnectivityMapFromCircuitJson(circuitJson);
|
|
222
835
|
let origin = options.origin;
|
|
223
836
|
if (!origin) {
|
|
@@ -228,6 +841,7 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
|
228
841
|
db,
|
|
229
842
|
project,
|
|
230
843
|
copperCutSetting,
|
|
844
|
+
throughBoardCutSetting,
|
|
231
845
|
connMap,
|
|
232
846
|
netGeoms: /* @__PURE__ */ new Map(),
|
|
233
847
|
origin
|
|
@@ -238,9 +852,15 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
|
238
852
|
for (const smtpad of db.pcb_smtpad.list()) {
|
|
239
853
|
addSmtPad(smtpad, ctx);
|
|
240
854
|
}
|
|
855
|
+
for (const platedHole of db.pcb_plated_hole.list()) {
|
|
856
|
+
addPlatedHole(platedHole, ctx);
|
|
857
|
+
}
|
|
241
858
|
for (const trace of db.pcb_trace.list()) {
|
|
242
859
|
addPcbTrace(trace, ctx);
|
|
243
860
|
}
|
|
861
|
+
for (const board of db.pcb_board.list()) {
|
|
862
|
+
addPcbBoard(board, ctx);
|
|
863
|
+
}
|
|
244
864
|
for (const net of Object.keys(connMap.netMap)) {
|
|
245
865
|
const netGeoms = ctx.netGeoms.get(net);
|
|
246
866
|
if (netGeoms.length === 0) {
|
|
@@ -248,19 +868,19 @@ var convertCircuitJsonToLbrn = (circuitJson, options = {}) => {
|
|
|
248
868
|
}
|
|
249
869
|
let union = netGeoms[0];
|
|
250
870
|
if (union instanceof Box2) {
|
|
251
|
-
union = new
|
|
871
|
+
union = new Polygon2(union);
|
|
252
872
|
}
|
|
253
873
|
for (const geom of netGeoms.slice(1)) {
|
|
254
|
-
if (geom instanceof
|
|
874
|
+
if (geom instanceof Polygon2) {
|
|
255
875
|
union = BooleanOperations2.unify(union, geom);
|
|
256
876
|
} else if (geom instanceof Box2) {
|
|
257
|
-
union = BooleanOperations2.unify(union, new
|
|
877
|
+
union = BooleanOperations2.unify(union, new Polygon2(geom));
|
|
258
878
|
}
|
|
259
879
|
}
|
|
260
880
|
for (const island of union.splitToIslands()) {
|
|
261
881
|
const { verts, prims } = polygonToShapePathData(island);
|
|
262
882
|
project.children.push(
|
|
263
|
-
new
|
|
883
|
+
new ShapePath10({
|
|
264
884
|
cutIndex: copperCutSetting.index,
|
|
265
885
|
verts,
|
|
266
886
|
prims,
|