circuit-json-to-lbrn 0.0.20 → 0.0.22
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 +1 -0
- package/dist/index.js +784 -345
- package/lib/ConvertContext.ts +6 -2
- package/lib/element-handlers/addPcbCutout/addCirclePcbCutout.ts +10 -2
- package/lib/element-handlers/addPcbCutout/addPolygonPcbCutout.ts +10 -6
- package/lib/element-handlers/addPcbCutout/addRectPcbCutout.ts +12 -12
- package/lib/element-handlers/addPcbHole/addCirclePcbHole.ts +10 -2
- package/lib/element-handlers/addPcbHole/addOvalPcbHole.ts +8 -8
- package/lib/element-handlers/addPcbHole/addPillPcbHole.ts +8 -8
- package/lib/element-handlers/addPcbHole/addRectPcbHole.ts +10 -10
- package/lib/element-handlers/addPcbHole/addRotatedPillPcbHole.ts +8 -8
- package/lib/element-handlers/addPcbTrace/index.ts +145 -61
- package/lib/element-handlers/addPcbVia/index.ts +49 -15
- package/lib/element-handlers/addPlatedHole/addCirclePlatedHole.ts +49 -15
- package/lib/element-handlers/addPlatedHole/addCircularHoleWithRectPad.ts +38 -18
- package/lib/element-handlers/addPlatedHole/addHoleWithPolygonPad.ts +41 -16
- package/lib/element-handlers/addPlatedHole/addOvalPlatedHole.ts +39 -18
- package/lib/element-handlers/addPlatedHole/addPillHoleWithRectPad.ts +38 -23
- package/lib/element-handlers/addPlatedHole/addPillPlatedHole.ts +39 -18
- package/lib/element-handlers/addPlatedHole/addRotatedPillHoleWithRectPad.ts +43 -28
- package/lib/element-handlers/addSmtPad/addCircleSmtPad.ts +31 -4
- package/lib/element-handlers/addSmtPad/addPillSmtPad.ts +33 -4
- package/lib/element-handlers/addSmtPad/addPolygonSmtPad.ts +25 -3
- package/lib/element-handlers/addSmtPad/addRectSmtPad.ts +20 -3
- package/lib/element-handlers/addSmtPad/addRotatedPillSmtPad.ts +31 -12
- package/lib/element-handlers/addSmtPad/addRotatedRectSmtPad.ts +31 -12
- package/lib/helpers/circleShape.ts +13 -6
- package/lib/helpers/ovalShape.ts +17 -8
- package/lib/helpers/pathPointUtils.ts +11 -5
- package/lib/helpers/pillShape.ts +24 -11
- package/lib/helpers/polygonShape.ts +11 -5
- package/lib/helpers/roundedRectShape.ts +19 -9
- package/lib/index.ts +92 -41
- package/package.json +1 -1
- package/tests/assets/keyboard-default60.json +92565 -0
- package/tests/examples/__snapshots__/board-outline-soldermask-preset.snap.svg +1 -1
- package/tests/examples/__snapshots__/board-outline.snap.svg +1 -1
- package/tests/examples/__snapshots__/lga-interconnect.snap.svg +1 -1
- package/tests/examples/__snapshots__/single-trace.snap.svg +1 -1
- package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-circle.snap.svg +1 -1
- package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-path.snap.svg +1 -1
- package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-polygon.snap.svg +1 -1
- package/tests/examples/addPcbCutout/__snapshots__/pcb-cutout-rect.snap.svg +1 -1
- package/tests/examples/addPcbHole/__snapshots__/pcb-hole-circle.snap.svg +1 -1
- package/tests/examples/addPcbHole/__snapshots__/pcb-hole-oval.snap.svg +1 -1
- package/tests/examples/addPcbHole/__snapshots__/pcb-hole-pill.snap.svg +1 -1
- package/tests/examples/addPcbHole/__snapshots__/pcb-hole-rect.snap.svg +1 -1
- package/tests/examples/addPcbHole/__snapshots__/pcb-hole-rotated-pill.snap.svg +2 -2
- package/tests/examples/addPcbHole/__snapshots__/pcb-hole-with-soldermask.snap.svg +1 -1
- package/tests/examples/addPcbVia/__snapshots__/pcb-via-basic.snap.svg +1 -1
- package/tests/examples/addPcbVia/__snapshots__/pcb-via-with-net.snap.svg +1 -1
- package/tests/examples/addPcbVia/__snapshots__/pcb-via-with-soldermask.snap.svg +1 -1
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-circle.snap.svg +1 -1
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-circular-hole-with-rect-pad.snap.svg +1 -1
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-oval.snap.svg +1 -1
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-pill-with-rect-pad.snap.svg +1 -1
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-pill.snap.svg +1 -1
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-polygon.snap.svg +1 -1
- package/tests/examples/addPlatedHole/__snapshots__/pcb-plated-hole-rotated-pill-with-rect-pad.snap.svg +1 -1
- package/tests/examples/addSmtPad/__snapshots__/circleSmtPad.snap.svg +1 -1
- package/tests/examples/addSmtPad/__snapshots__/pillSmtPad.snap.svg +1 -1
- package/tests/examples/addSmtPad/__snapshots__/polygonSmtPad.snap.svg +1 -1
- package/tests/examples/addSmtPad/__snapshots__/rotatedPillSmtPad.snap.svg +1 -1
- package/tests/examples/addSmtPad/__snapshots__/rotatedRectSmtPad.snap.svg +1 -1
- package/tests/examples/keyboard-defaul60/__snapshots__/keyboard-both-layer-includeSoldermask.snap.svg +8 -0
- package/tests/examples/keyboard-defaul60/__snapshots__/keyboard-both-layers.snap.svg +8 -0
- package/tests/examples/keyboard-defaul60/__snapshots__/keyboard-bottom-layer.snap.svg +8 -0
- package/tests/examples/keyboard-defaul60/__snapshots__/keyboard-top-layer.snap.svg +8 -0
- package/tests/examples/keyboard-defaul60/keyboard-both-layer-includeSoldermask.test.ts +27 -0
- package/tests/examples/keyboard-defaul60/keyboard-both-layers.test.ts +26 -0
- package/tests/examples/keyboard-defaul60/keyboard-bottom-layer.test.ts +26 -0
- package/tests/examples/keyboard-defaul60/keyboard-top-layer.test.ts +26 -0
- package/tests/examples/lga-interconnect.test.ts +3 -2
- package/tests/examples/soldermask/__snapshots__/copper-and-soldermask.snap.svg +1 -1
- package/tests/examples/soldermask/__snapshots__/copper-only.snap.svg +1 -1
- package/tests/examples/soldermask/__snapshots__/soldermask-only.snap.svg +1 -1
- package/tests/examples/soldermask/copper-and-soldermask.test.ts +18 -10
- package/tests/examples/soldermask/soldermask-only.test.ts +3 -3
- package/tests/examples/soldermask-margin/__snapshots__/negative-soldermask-margin.snap.svg +1 -1
- package/tests/examples/soldermask-margin/__snapshots__/positive-soldermask-margin.snap.svg +1 -1
- package/tsconfig.json +2 -1
package/lib/ConvertContext.ts
CHANGED
|
@@ -9,19 +9,23 @@ export interface ConvertContext {
|
|
|
9
9
|
db: CircuitJsonUtilObjects
|
|
10
10
|
project: LightBurnProject
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
topCopperCutSetting: CutSetting
|
|
13
|
+
bottomCopperCutSetting: CutSetting
|
|
13
14
|
throughBoardCutSetting: CutSetting
|
|
14
15
|
soldermaskCutSetting: CutSetting
|
|
15
16
|
|
|
16
17
|
connMap: ConnectivityMap
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
// Separate net geometries for top and bottom layers
|
|
20
|
+
topNetGeoms: Map<ConnectivityMapKey, Array<Polygon | Box>>
|
|
21
|
+
bottomNetGeoms: Map<ConnectivityMapKey, Array<Polygon | Box>>
|
|
19
22
|
|
|
20
23
|
origin: { x: number; y: number }
|
|
21
24
|
|
|
22
25
|
// Include flags
|
|
23
26
|
includeCopper: boolean
|
|
24
27
|
includeSoldermask: boolean
|
|
28
|
+
includeLayers: Array<"top" | "bottom">
|
|
25
29
|
|
|
26
30
|
// Soldermask margin (can be negative)
|
|
27
31
|
soldermaskMargin: number
|
|
@@ -25,7 +25,11 @@ export const addCirclePcbCutout = (
|
|
|
25
25
|
|
|
26
26
|
// Add the cutout - cut through the board
|
|
27
27
|
if (cutout.radius > 0 && includeCopper) {
|
|
28
|
-
const circlePath = createCirclePath(
|
|
28
|
+
const circlePath = createCirclePath({
|
|
29
|
+
centerX,
|
|
30
|
+
centerY,
|
|
31
|
+
radius: cutout.radius,
|
|
32
|
+
})
|
|
29
33
|
project.children.push(
|
|
30
34
|
new ShapePath({
|
|
31
35
|
cutIndex: throughBoardCutSetting.index,
|
|
@@ -39,7 +43,11 @@ export const addCirclePcbCutout = (
|
|
|
39
43
|
// Add soldermask opening if drawing soldermask
|
|
40
44
|
if (cutout.radius > 0 && includeSoldermask) {
|
|
41
45
|
const smRadius = cutout.radius + soldermaskMargin
|
|
42
|
-
const outer = createCirclePath(
|
|
46
|
+
const outer = createCirclePath({
|
|
47
|
+
centerX,
|
|
48
|
+
centerY,
|
|
49
|
+
radius: smRadius,
|
|
50
|
+
})
|
|
43
51
|
project.children.push(
|
|
44
52
|
new ShapePath({
|
|
45
53
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -23,11 +23,11 @@ export const addPolygonPcbCutout = (
|
|
|
23
23
|
|
|
24
24
|
// Add the cutout - cut through the board
|
|
25
25
|
if (cutout.points.length >= 3 && includeCopper) {
|
|
26
|
-
const polygonPath = createPolygonPathFromOutline(
|
|
27
|
-
cutout.points,
|
|
28
|
-
origin.x,
|
|
29
|
-
origin.y,
|
|
30
|
-
)
|
|
26
|
+
const polygonPath = createPolygonPathFromOutline({
|
|
27
|
+
outline: cutout.points,
|
|
28
|
+
offsetX: origin.x,
|
|
29
|
+
offsetY: origin.y,
|
|
30
|
+
})
|
|
31
31
|
project.children.push(
|
|
32
32
|
new ShapePath({
|
|
33
33
|
cutIndex: throughBoardCutSetting.index,
|
|
@@ -55,7 +55,11 @@ export const addPolygonPcbCutout = (
|
|
|
55
55
|
}))
|
|
56
56
|
: cutout.points
|
|
57
57
|
|
|
58
|
-
const polygonPath = createPolygonPathFromOutline(
|
|
58
|
+
const polygonPath = createPolygonPathFromOutline({
|
|
59
|
+
outline: points,
|
|
60
|
+
offsetX: origin.x,
|
|
61
|
+
offsetY: origin.y,
|
|
62
|
+
})
|
|
59
63
|
project.children.push(
|
|
60
64
|
new ShapePath({
|
|
61
65
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -26,15 +26,15 @@ export const addRectPcbCutout = (
|
|
|
26
26
|
// Add the cutout - cut through the board
|
|
27
27
|
if (cutout.width > 0 && cutout.height > 0 && includeCopper) {
|
|
28
28
|
const rotation = (cutout.rotation ?? 0) * (Math.PI / 180) // Convert degrees to radians
|
|
29
|
-
const rectPath = createRoundedRectPath(
|
|
29
|
+
const rectPath = createRoundedRectPath({
|
|
30
30
|
centerX,
|
|
31
31
|
centerY,
|
|
32
|
-
cutout.width,
|
|
33
|
-
cutout.height,
|
|
34
|
-
0, // no border radius for cutouts
|
|
35
|
-
4, // segments
|
|
32
|
+
width: cutout.width,
|
|
33
|
+
height: cutout.height,
|
|
34
|
+
borderRadius: 0, // no border radius for cutouts
|
|
35
|
+
segments: 4, // segments
|
|
36
36
|
rotation,
|
|
37
|
-
)
|
|
37
|
+
})
|
|
38
38
|
project.children.push(
|
|
39
39
|
new ShapePath({
|
|
40
40
|
cutIndex: throughBoardCutSetting.index,
|
|
@@ -50,15 +50,15 @@ export const addRectPcbCutout = (
|
|
|
50
50
|
const rotation = (cutout.rotation ?? 0) * (Math.PI / 180) // Convert degrees to radians
|
|
51
51
|
const smWidth = cutout.width + 2 * soldermaskMargin
|
|
52
52
|
const smHeight = cutout.height + 2 * soldermaskMargin
|
|
53
|
-
const rectPath = createRoundedRectPath(
|
|
53
|
+
const rectPath = createRoundedRectPath({
|
|
54
54
|
centerX,
|
|
55
55
|
centerY,
|
|
56
|
-
smWidth,
|
|
57
|
-
smHeight,
|
|
58
|
-
0, // no border radius for cutouts
|
|
59
|
-
4, // segments
|
|
56
|
+
width: smWidth,
|
|
57
|
+
height: smHeight,
|
|
58
|
+
borderRadius: 0, // no border radius for cutouts
|
|
59
|
+
segments: 4, // segments
|
|
60
60
|
rotation,
|
|
61
|
-
)
|
|
61
|
+
})
|
|
62
62
|
project.children.push(
|
|
63
63
|
new ShapePath({
|
|
64
64
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -25,7 +25,11 @@ export const addCirclePcbHole = (
|
|
|
25
25
|
// Add soldermask opening if drawing soldermask
|
|
26
26
|
if (hole.hole_diameter > 0 && includeSoldermask) {
|
|
27
27
|
const smRadius = hole.hole_diameter / 2 + soldermaskMargin
|
|
28
|
-
const soldermaskPath = createCirclePath(
|
|
28
|
+
const soldermaskPath = createCirclePath({
|
|
29
|
+
centerX,
|
|
30
|
+
centerY,
|
|
31
|
+
radius: smRadius,
|
|
32
|
+
})
|
|
29
33
|
project.children.push(
|
|
30
34
|
new ShapePath({
|
|
31
35
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -39,7 +43,11 @@ export const addCirclePcbHole = (
|
|
|
39
43
|
// Add the hole - cut through the board
|
|
40
44
|
if (hole.hole_diameter > 0 && includeCopper) {
|
|
41
45
|
const radius = hole.hole_diameter / 2
|
|
42
|
-
const circlePath = createCirclePath(
|
|
46
|
+
const circlePath = createCirclePath({
|
|
47
|
+
centerX,
|
|
48
|
+
centerY,
|
|
49
|
+
radius,
|
|
50
|
+
})
|
|
43
51
|
project.children.push(
|
|
44
52
|
new ShapePath({
|
|
45
53
|
cutIndex: throughBoardCutSetting.index,
|
|
@@ -24,12 +24,12 @@ export const addOvalPcbHole = (
|
|
|
24
24
|
|
|
25
25
|
// Add soldermask opening if drawing soldermask
|
|
26
26
|
if (hole.hole_width > 0 && hole.hole_height > 0 && includeSoldermask) {
|
|
27
|
-
const soldermaskPath = createOvalPath(
|
|
27
|
+
const soldermaskPath = createOvalPath({
|
|
28
28
|
centerX,
|
|
29
29
|
centerY,
|
|
30
|
-
hole.hole_width + soldermaskMargin * 2,
|
|
31
|
-
hole.hole_height + soldermaskMargin * 2,
|
|
32
|
-
)
|
|
30
|
+
width: hole.hole_width + soldermaskMargin * 2,
|
|
31
|
+
height: hole.hole_height + soldermaskMargin * 2,
|
|
32
|
+
})
|
|
33
33
|
project.children.push(
|
|
34
34
|
new ShapePath({
|
|
35
35
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -42,12 +42,12 @@ export const addOvalPcbHole = (
|
|
|
42
42
|
|
|
43
43
|
// Add the hole - cut through the board
|
|
44
44
|
if (hole.hole_width > 0 && hole.hole_height > 0 && includeCopper) {
|
|
45
|
-
const ovalPath = createOvalPath(
|
|
45
|
+
const ovalPath = createOvalPath({
|
|
46
46
|
centerX,
|
|
47
47
|
centerY,
|
|
48
|
-
hole.hole_width,
|
|
49
|
-
hole.hole_height,
|
|
50
|
-
)
|
|
48
|
+
width: hole.hole_width,
|
|
49
|
+
height: hole.hole_height,
|
|
50
|
+
})
|
|
51
51
|
project.children.push(
|
|
52
52
|
new ShapePath({
|
|
53
53
|
cutIndex: throughBoardCutSetting.index,
|
|
@@ -24,12 +24,12 @@ export const addPillPcbHole = (
|
|
|
24
24
|
|
|
25
25
|
// Add soldermask opening if drawing soldermask
|
|
26
26
|
if (hole.hole_width > 0 && hole.hole_height > 0 && includeSoldermask) {
|
|
27
|
-
const soldermaskPath = createPillPath(
|
|
27
|
+
const soldermaskPath = createPillPath({
|
|
28
28
|
centerX,
|
|
29
29
|
centerY,
|
|
30
|
-
hole.hole_width + soldermaskMargin * 2,
|
|
31
|
-
hole.hole_height + soldermaskMargin * 2,
|
|
32
|
-
)
|
|
30
|
+
width: hole.hole_width + soldermaskMargin * 2,
|
|
31
|
+
height: hole.hole_height + soldermaskMargin * 2,
|
|
32
|
+
})
|
|
33
33
|
project.children.push(
|
|
34
34
|
new ShapePath({
|
|
35
35
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -42,12 +42,12 @@ export const addPillPcbHole = (
|
|
|
42
42
|
|
|
43
43
|
// Add the hole - cut through the board
|
|
44
44
|
if (hole.hole_width > 0 && hole.hole_height > 0 && includeCopper) {
|
|
45
|
-
const pillPath = createPillPath(
|
|
45
|
+
const pillPath = createPillPath({
|
|
46
46
|
centerX,
|
|
47
47
|
centerY,
|
|
48
|
-
hole.hole_width,
|
|
49
|
-
hole.hole_height,
|
|
50
|
-
)
|
|
48
|
+
width: hole.hole_width,
|
|
49
|
+
height: hole.hole_height,
|
|
50
|
+
})
|
|
51
51
|
project.children.push(
|
|
52
52
|
new ShapePath({
|
|
53
53
|
cutIndex: throughBoardCutSetting.index,
|
|
@@ -24,13 +24,13 @@ export const addRectPcbHole = (
|
|
|
24
24
|
|
|
25
25
|
// Add soldermask opening if drawing soldermask
|
|
26
26
|
if (hole.hole_width > 0 && hole.hole_height > 0 && includeSoldermask) {
|
|
27
|
-
const soldermaskPath = createRoundedRectPath(
|
|
27
|
+
const soldermaskPath = createRoundedRectPath({
|
|
28
28
|
centerX,
|
|
29
29
|
centerY,
|
|
30
|
-
hole.hole_width + soldermaskMargin * 2,
|
|
31
|
-
hole.hole_height + soldermaskMargin * 2,
|
|
32
|
-
0, // no border radius for rect holes
|
|
33
|
-
)
|
|
30
|
+
width: hole.hole_width + soldermaskMargin * 2,
|
|
31
|
+
height: hole.hole_height + soldermaskMargin * 2,
|
|
32
|
+
borderRadius: 0, // no border radius for rect holes
|
|
33
|
+
})
|
|
34
34
|
project.children.push(
|
|
35
35
|
new ShapePath({
|
|
36
36
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -43,13 +43,13 @@ export const addRectPcbHole = (
|
|
|
43
43
|
|
|
44
44
|
// Add the hole - cut through the board
|
|
45
45
|
if (hole.hole_width > 0 && hole.hole_height > 0 && includeCopper) {
|
|
46
|
-
const rectPath = createRoundedRectPath(
|
|
46
|
+
const rectPath = createRoundedRectPath({
|
|
47
47
|
centerX,
|
|
48
48
|
centerY,
|
|
49
|
-
hole.hole_width,
|
|
50
|
-
hole.hole_height,
|
|
51
|
-
0, // no border radius for rect holes
|
|
52
|
-
)
|
|
49
|
+
width: hole.hole_width,
|
|
50
|
+
height: hole.hole_height,
|
|
51
|
+
borderRadius: 0, // no border radius for rect holes
|
|
52
|
+
})
|
|
53
53
|
project.children.push(
|
|
54
54
|
new ShapePath({
|
|
55
55
|
cutIndex: throughBoardCutSetting.index,
|
|
@@ -25,13 +25,13 @@ export const addRotatedPillPcbHole = (
|
|
|
25
25
|
|
|
26
26
|
// Add soldermask opening if drawing soldermask
|
|
27
27
|
if (hole.hole_width > 0 && hole.hole_height > 0 && includeSoldermask) {
|
|
28
|
-
const soldermaskPath = createPillPath(
|
|
28
|
+
const soldermaskPath = createPillPath({
|
|
29
29
|
centerX,
|
|
30
30
|
centerY,
|
|
31
|
-
hole.hole_width + soldermaskMargin * 2,
|
|
32
|
-
hole.hole_height + soldermaskMargin * 2,
|
|
31
|
+
width: hole.hole_width + soldermaskMargin * 2,
|
|
32
|
+
height: hole.hole_height + soldermaskMargin * 2,
|
|
33
33
|
rotation,
|
|
34
|
-
)
|
|
34
|
+
})
|
|
35
35
|
project.children.push(
|
|
36
36
|
new ShapePath({
|
|
37
37
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -44,13 +44,13 @@ export const addRotatedPillPcbHole = (
|
|
|
44
44
|
|
|
45
45
|
// Add the hole - cut through the board
|
|
46
46
|
if (hole.hole_width > 0 && hole.hole_height > 0 && includeCopper) {
|
|
47
|
-
const pillPath = createPillPath(
|
|
47
|
+
const pillPath = createPillPath({
|
|
48
48
|
centerX,
|
|
49
49
|
centerY,
|
|
50
|
-
hole.hole_width,
|
|
51
|
-
hole.hole_height,
|
|
50
|
+
width: hole.hole_width,
|
|
51
|
+
height: hole.hole_height,
|
|
52
52
|
rotation,
|
|
53
|
-
)
|
|
53
|
+
})
|
|
54
54
|
project.children.push(
|
|
55
55
|
new ShapePath({
|
|
56
56
|
cutIndex: throughBoardCutSetting.index,
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import type { PcbTrace } from "circuit-json"
|
|
1
|
+
import type { PcbTrace, PcbTraceRoutePoint } from "circuit-json"
|
|
2
2
|
import type { ConvertContext } from "../../ConvertContext"
|
|
3
3
|
import Flatten, { BooleanOperations } from "@flatten-js/core"
|
|
4
4
|
import { circleToPolygon } from "./circle-to-polygon"
|
|
5
5
|
|
|
6
6
|
export const addPcbTrace = (trace: PcbTrace, ctx: ConvertContext) => {
|
|
7
|
-
const {
|
|
7
|
+
const {
|
|
8
|
+
topNetGeoms,
|
|
9
|
+
bottomNetGeoms,
|
|
10
|
+
connMap,
|
|
11
|
+
origin,
|
|
12
|
+
includeCopper,
|
|
13
|
+
includeLayers,
|
|
14
|
+
} = ctx
|
|
8
15
|
|
|
9
16
|
// Only include traces when including copper
|
|
10
17
|
// Traces are NOT included in soldermask-only mode to prevent accidental bridging
|
|
@@ -14,83 +21,160 @@ export const addPcbTrace = (trace: PcbTrace, ctx: ConvertContext) => {
|
|
|
14
21
|
|
|
15
22
|
const netId = connMap.getNetConnectedToId(
|
|
16
23
|
trace.source_trace_id ?? trace.pcb_trace_id,
|
|
17
|
-
)
|
|
24
|
+
)
|
|
18
25
|
|
|
19
26
|
if (!netId) {
|
|
20
|
-
|
|
27
|
+
// Skip traces that are not connected to any net
|
|
28
|
+
// This can happen for traces without a source_trace_id
|
|
21
29
|
return
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
if (!trace.route || trace.route.length < 2) {
|
|
25
|
-
console.warn(`Trace ${trace.pcb_trace_id} has insufficient route points`)
|
|
26
33
|
return
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
const { route } = trace
|
|
37
|
+
const wirePoint = route.find((point) => {
|
|
38
|
+
if (!("route_type" in point)) return true
|
|
39
|
+
return point.route_type === "wire"
|
|
40
|
+
})
|
|
41
|
+
const traceWidth = (wirePoint as any)?.width ?? 0.15
|
|
42
|
+
|
|
43
|
+
// Group consecutive wire segments by layer
|
|
44
|
+
// When we hit a via, we start a new segment on the new layer
|
|
45
|
+
const layerSegments = new Map<
|
|
46
|
+
"top" | "bottom",
|
|
47
|
+
Array<Array<{ x: number; y: number }>>
|
|
48
|
+
>()
|
|
49
|
+
|
|
50
|
+
let currentSegment: Array<{ x: number; y: number }> = []
|
|
51
|
+
let currentLayer: "top" | "bottom" | null = null
|
|
52
|
+
|
|
53
|
+
for (const point of route) {
|
|
54
|
+
if ("route_type" in point && point.route_type === "via") {
|
|
55
|
+
// Via marks end of current segment - save it
|
|
56
|
+
if (currentLayer && currentSegment.length > 0) {
|
|
57
|
+
if (!layerSegments.has(currentLayer)) {
|
|
58
|
+
layerSegments.set(currentLayer, [])
|
|
59
|
+
}
|
|
60
|
+
layerSegments.get(currentLayer)!.push(currentSegment)
|
|
61
|
+
}
|
|
62
|
+
// Reset for next segment
|
|
63
|
+
currentSegment = []
|
|
64
|
+
currentLayer = null
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
30
67
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
68
|
+
// Treat points without route_type as wire points (default behavior)
|
|
69
|
+
const isWirePoint = !("route_type" in point) || point.route_type === "wire"
|
|
70
|
+
if (isWirePoint && "layer" in point && point.layer) {
|
|
71
|
+
const pointLayer = point.layer as "top" | "bottom" | string
|
|
72
|
+
if (pointLayer !== "top" && pointLayer !== "bottom") {
|
|
73
|
+
continue // Skip inner layers
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If layer changed (shouldn't happen without via, but handle it)
|
|
77
|
+
if (currentLayer !== null && currentLayer !== pointLayer) {
|
|
78
|
+
// Save current segment
|
|
79
|
+
if (currentSegment.length > 0) {
|
|
80
|
+
if (!layerSegments.has(currentLayer)) {
|
|
81
|
+
layerSegments.set(currentLayer, [])
|
|
82
|
+
}
|
|
83
|
+
layerSegments.get(currentLayer)!.push(currentSegment)
|
|
84
|
+
}
|
|
85
|
+
// Start new segment
|
|
86
|
+
currentSegment = []
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
currentLayer = pointLayer
|
|
90
|
+
currentSegment.push({ x: point.x, y: point.y })
|
|
91
|
+
}
|
|
43
92
|
}
|
|
44
93
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const segmentLength = Math.hypot(p1.x - p2.x, p1.y - p2.y)
|
|
53
|
-
if (segmentLength === 0) continue
|
|
54
|
-
|
|
55
|
-
const centerX = (p1.x + p2.x) / 2 + origin.x
|
|
56
|
-
const centerY = (p1.y + p2.y) / 2 + origin.y
|
|
57
|
-
const rotationDeg = (Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180) / Math.PI
|
|
58
|
-
|
|
59
|
-
const w2 = segmentLength / 2
|
|
60
|
-
const h2 = traceWidth / 2
|
|
61
|
-
|
|
62
|
-
const angleRad = (rotationDeg * Math.PI) / 180
|
|
63
|
-
const cosAngle = Math.cos(angleRad)
|
|
64
|
-
const sinAngle = Math.sin(angleRad)
|
|
65
|
-
|
|
66
|
-
const corners = [
|
|
67
|
-
{ x: -w2, y: -h2 },
|
|
68
|
-
{ x: w2, y: -h2 },
|
|
69
|
-
{ x: w2, y: h2 },
|
|
70
|
-
{ x: -w2, y: h2 },
|
|
71
|
-
]
|
|
94
|
+
// Save final segment
|
|
95
|
+
if (currentLayer && currentSegment.length > 0) {
|
|
96
|
+
if (!layerSegments.has(currentLayer)) {
|
|
97
|
+
layerSegments.set(currentLayer, [])
|
|
98
|
+
}
|
|
99
|
+
layerSegments.get(currentLayer)!.push(currentSegment)
|
|
100
|
+
}
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
102
|
+
// Process each layer
|
|
103
|
+
for (const [layer, segments] of layerSegments.entries()) {
|
|
104
|
+
if (!includeLayers.includes(layer)) {
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
77
107
|
|
|
78
|
-
polygons
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
108
|
+
// Combine all segments for this layer into one set of polygons
|
|
109
|
+
const polygons: Flatten.Polygon[] = []
|
|
110
|
+
|
|
111
|
+
for (const points of segments) {
|
|
112
|
+
if (points.length < 2) continue
|
|
113
|
+
|
|
114
|
+
// Add circles for each vertex in this segment
|
|
115
|
+
for (const routePoint of points) {
|
|
116
|
+
const circle = new Flatten.Circle(
|
|
117
|
+
new Flatten.Point(routePoint.x + origin.x, routePoint.y + origin.y),
|
|
118
|
+
traceWidth / 2,
|
|
119
|
+
)
|
|
120
|
+
polygons.push(circleToPolygon(circle))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Add rectangles for each line segment
|
|
124
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
125
|
+
const p1 = points[i]
|
|
126
|
+
const p2 = points[i + 1]
|
|
127
|
+
|
|
128
|
+
if (!p1 || !p2) continue
|
|
129
|
+
|
|
130
|
+
const segmentLength = Math.hypot(p1.x - p2.x, p1.y - p2.y)
|
|
131
|
+
if (segmentLength === 0) continue
|
|
132
|
+
|
|
133
|
+
const centerX = (p1.x + p2.x) / 2 + origin.x
|
|
134
|
+
const centerY = (p1.y + p2.y) / 2 + origin.y
|
|
135
|
+
const rotationDeg =
|
|
136
|
+
(Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180) / Math.PI
|
|
137
|
+
|
|
138
|
+
const w2 = segmentLength / 2
|
|
139
|
+
const h2 = traceWidth / 2
|
|
140
|
+
|
|
141
|
+
const angleRad = (rotationDeg * Math.PI) / 180
|
|
142
|
+
const cosAngle = Math.cos(angleRad)
|
|
143
|
+
const sinAngle = Math.sin(angleRad)
|
|
144
|
+
|
|
145
|
+
const corners = [
|
|
146
|
+
{ x: -w2, y: -h2 },
|
|
147
|
+
{ x: w2, y: -h2 },
|
|
148
|
+
{ x: w2, y: h2 },
|
|
149
|
+
{ x: -w2, y: h2 },
|
|
150
|
+
]
|
|
151
|
+
|
|
152
|
+
const rotatedCorners = corners.map((p) => ({
|
|
153
|
+
x: centerX + p.x * cosAngle - p.y * sinAngle,
|
|
154
|
+
y: centerY + p.x * sinAngle + p.y * cosAngle,
|
|
155
|
+
}))
|
|
156
|
+
|
|
157
|
+
polygons.push(
|
|
158
|
+
new Flatten.Polygon(
|
|
159
|
+
rotatedCorners.map((p) => Flatten.point(p.x, p.y)),
|
|
160
|
+
),
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
82
164
|
|
|
83
|
-
|
|
84
|
-
|
|
165
|
+
// Union all polygons together to create the final trace polygon for this layer
|
|
166
|
+
if (polygons.length === 0) continue
|
|
85
167
|
|
|
86
|
-
|
|
168
|
+
let tracePolygon = polygons[0]!
|
|
87
169
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
170
|
+
for (let i = 1; i < polygons.length; i++) {
|
|
171
|
+
const poly = polygons[i]
|
|
172
|
+
if (poly) {
|
|
173
|
+
tracePolygon = BooleanOperations.unify(tracePolygon, poly)
|
|
174
|
+
}
|
|
92
175
|
}
|
|
93
|
-
}
|
|
94
176
|
|
|
95
|
-
|
|
177
|
+
const netGeoms = layer === "top" ? topNetGeoms : bottomNetGeoms
|
|
178
|
+
netGeoms.get(netId)?.push(tracePolygon)
|
|
179
|
+
}
|
|
96
180
|
}
|
|
@@ -9,19 +9,24 @@ export const addPcbVia = (via: PcbVia, ctx: ConvertContext): void => {
|
|
|
9
9
|
const {
|
|
10
10
|
db,
|
|
11
11
|
project,
|
|
12
|
-
|
|
12
|
+
topCopperCutSetting,
|
|
13
|
+
bottomCopperCutSetting,
|
|
13
14
|
soldermaskCutSetting,
|
|
14
15
|
throughBoardCutSetting,
|
|
16
|
+
topNetGeoms,
|
|
17
|
+
bottomNetGeoms,
|
|
15
18
|
origin,
|
|
16
19
|
includeCopper,
|
|
17
20
|
includeSoldermask,
|
|
18
21
|
connMap,
|
|
19
22
|
soldermaskMargin,
|
|
23
|
+
includeLayers,
|
|
20
24
|
} = ctx
|
|
21
25
|
const centerX = via.x + origin.x
|
|
22
26
|
const centerY = via.y + origin.y
|
|
23
27
|
|
|
24
28
|
// Add outer circle (copper annulus) if drawing copper - add to netGeoms for merging
|
|
29
|
+
// Vias go through all layers, so add to both top and bottom
|
|
25
30
|
if (via.outer_diameter > 0 && includeCopper) {
|
|
26
31
|
// Find the pcb_port associated with this via (vias don't have pcb_port_id property)
|
|
27
32
|
// We need to find a port at the same location as the via
|
|
@@ -38,26 +43,51 @@ export const addPcbVia = (via: PcbVia, ctx: ConvertContext): void => {
|
|
|
38
43
|
const polygon = circleToPolygon(circle)
|
|
39
44
|
|
|
40
45
|
if (netId) {
|
|
41
|
-
// Add to
|
|
42
|
-
|
|
46
|
+
// Add to both top and bottom netGeoms since vias go through the board
|
|
47
|
+
if (includeLayers.includes("top")) {
|
|
48
|
+
topNetGeoms.get(netId)?.push(polygon.clone())
|
|
49
|
+
}
|
|
50
|
+
if (includeLayers.includes("bottom")) {
|
|
51
|
+
bottomNetGeoms.get(netId)?.push(polygon.clone())
|
|
52
|
+
}
|
|
43
53
|
} else {
|
|
44
|
-
// No net connection - draw directly
|
|
45
|
-
const outer = createCirclePath(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
// No net connection - draw directly for each included layer
|
|
55
|
+
const outer = createCirclePath({
|
|
56
|
+
centerX,
|
|
57
|
+
centerY,
|
|
58
|
+
radius: outerRadius,
|
|
59
|
+
})
|
|
60
|
+
if (includeLayers.includes("top")) {
|
|
61
|
+
project.children.push(
|
|
62
|
+
new ShapePath({
|
|
63
|
+
cutIndex: topCopperCutSetting.index,
|
|
64
|
+
verts: outer.verts,
|
|
65
|
+
prims: outer.prims,
|
|
66
|
+
isClosed: true,
|
|
67
|
+
}),
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
if (includeLayers.includes("bottom")) {
|
|
71
|
+
project.children.push(
|
|
72
|
+
new ShapePath({
|
|
73
|
+
cutIndex: bottomCopperCutSetting.index,
|
|
74
|
+
verts: outer.verts,
|
|
75
|
+
prims: outer.prims,
|
|
76
|
+
isClosed: true,
|
|
77
|
+
}),
|
|
78
|
+
)
|
|
79
|
+
}
|
|
54
80
|
}
|
|
55
81
|
}
|
|
56
82
|
|
|
57
83
|
// Add soldermask opening if drawing soldermask
|
|
58
84
|
if (via.outer_diameter > 0 && includeSoldermask) {
|
|
59
85
|
const smRadius = via.outer_diameter / 2 + soldermaskMargin
|
|
60
|
-
const outer = createCirclePath(
|
|
86
|
+
const outer = createCirclePath({
|
|
87
|
+
centerX,
|
|
88
|
+
centerY,
|
|
89
|
+
radius: smRadius,
|
|
90
|
+
})
|
|
61
91
|
project.children.push(
|
|
62
92
|
new ShapePath({
|
|
63
93
|
cutIndex: soldermaskCutSetting.index,
|
|
@@ -71,7 +101,11 @@ export const addPcbVia = (via: PcbVia, ctx: ConvertContext): void => {
|
|
|
71
101
|
// Add inner circle (hole) - always cut through the board regardless of mode
|
|
72
102
|
if (via.hole_diameter > 0 && includeCopper) {
|
|
73
103
|
const innerRadius = via.hole_diameter / 2
|
|
74
|
-
const inner = createCirclePath(
|
|
104
|
+
const inner = createCirclePath({
|
|
105
|
+
centerX,
|
|
106
|
+
centerY,
|
|
107
|
+
radius: innerRadius,
|
|
108
|
+
})
|
|
75
109
|
project.children.push(
|
|
76
110
|
new ShapePath({
|
|
77
111
|
cutIndex: throughBoardCutSetting.index,
|