circuit-to-canvas 0.0.49 → 0.0.51
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 +13 -5
- package/dist/index.js +1450 -1226
- package/lib/drawer/CircuitToCanvasDrawer.ts +262 -312
- package/lib/drawer/elements/helper-functions/draw-pill.ts +39 -0
- package/lib/drawer/elements/helper-functions/draw-polygon.ts +25 -0
- package/lib/drawer/elements/helper-functions/draw-rounded-rect.ts +34 -0
- package/lib/drawer/elements/helper-functions/index.ts +3 -0
- package/lib/drawer/elements/pcb-board.ts +13 -3
- package/lib/drawer/elements/pcb-hole.ts +56 -338
- package/lib/drawer/elements/pcb-plated-hole.ts +154 -442
- package/lib/drawer/elements/pcb-smtpad.ts +5 -271
- package/lib/drawer/elements/pcb-soldermask/board.ts +44 -0
- package/lib/drawer/elements/pcb-soldermask/cutout.ts +74 -0
- package/lib/drawer/elements/pcb-soldermask/hole.ts +288 -0
- package/lib/drawer/elements/pcb-soldermask/index.ts +140 -0
- package/lib/drawer/elements/pcb-soldermask/plated-hole.ts +365 -0
- package/lib/drawer/elements/pcb-soldermask/smt-pad.ts +354 -0
- package/lib/drawer/elements/pcb-soldermask/via.ts +27 -0
- package/lib/drawer/elements/soldermask-margin.ts +39 -8
- package/package.json +2 -2
- package/tests/board-snapshot/__snapshots__/usb-c-flashlight-board.snap.png +0 -0
- package/tests/board-snapshot/usb-c-flashlight-board.test.ts +1 -0
- package/tests/elements/__snapshots__/board-with-elements.snap.png +0 -0
- package/tests/elements/__snapshots__/brep-copper-pours.snap.png +0 -0
- package/tests/elements/__snapshots__/custom-outline-board.snap.png +0 -0
- package/tests/elements/__snapshots__/oval-plated-hole.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-board.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-comprehensive-soldermask-margin.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-dimension.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-hole-soldermask-margin.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-keepout-layer-filter.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-keepout-multiple-layers.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-keepout-rect-and-circle.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-keepout-with-group-id.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-no-soldermask.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-plated-hole-soldermask-margin.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-plated-hole.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-silkscreen-on-component.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-silkscreen-oval.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-smtpad-asymmetric-soldermask-margin.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-smtpad-soldermask-coverage.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-smtpad-soldermask-margin.snap.png +0 -0
- package/tests/elements/__snapshots__/pill-plated-hole.snap.png +0 -0
- package/tests/elements/pcb-comprehensive-soldermask-margin.test.ts +2 -2
- package/tests/elements/pcb-hole-soldermask-margin.test.ts +155 -2
- package/tests/elements/pcb-no-soldermask.test.ts +1281 -0
- package/tests/elements/pcb-plated-hole-soldermask-margin.test.ts +1 -1
- package/tests/elements/pcb-plated-hole.test.ts +40 -4
- package/tests/elements/pcb-smtpad-asymmetric-soldermask-margin.test.ts +140 -0
- package/tests/elements/pcb-smtpad-soldermask-coverage.test.ts +1 -1
- package/tests/elements/pcb-smtpad-soldermask-margin.test.ts +18 -2
- package/tests/fixtures/getStackedPngSvgComparison.ts +8 -2
|
@@ -217,7 +217,7 @@ test("draw plated holes with positive and negative soldermask margins", async ()
|
|
|
217
217
|
]
|
|
218
218
|
|
|
219
219
|
drawer.setCameraBounds({ minX: -8, maxX: 8, minY: -6.5, maxY: 6.5 })
|
|
220
|
-
drawer.drawElements(circuit)
|
|
220
|
+
drawer.drawElements(circuit, { showSoldermask: true })
|
|
221
221
|
|
|
222
222
|
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
223
223
|
import.meta.path,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expect, test } from "bun:test"
|
|
2
2
|
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
-
import type { PcbPlatedHole } from "circuit-json"
|
|
3
|
+
import type { PcbBoard, PcbPlatedHole } from "circuit-json"
|
|
4
4
|
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
5
5
|
|
|
6
6
|
test("draw circular plated hole", async () => {
|
|
@@ -22,7 +22,19 @@ test("draw circular plated hole", async () => {
|
|
|
22
22
|
layers: ["top", "bottom"],
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const board: PcbBoard = {
|
|
26
|
+
type: "pcb_board",
|
|
27
|
+
pcb_board_id: "board1",
|
|
28
|
+
center: { x: 50, y: 50 },
|
|
29
|
+
width: 100,
|
|
30
|
+
height: 100,
|
|
31
|
+
thickness: 1.6,
|
|
32
|
+
num_layers: 2,
|
|
33
|
+
material: "fr4",
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
drawer.setCameraBounds({ minX: 0, maxX: 100, minY: 0, maxY: 100 })
|
|
37
|
+
drawer.drawElements([board, hole])
|
|
26
38
|
|
|
27
39
|
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
28
40
|
import.meta.path,
|
|
@@ -51,7 +63,19 @@ test("draw oval plated hole", async () => {
|
|
|
51
63
|
ccw_rotation: 0,
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
|
|
66
|
+
const board: PcbBoard = {
|
|
67
|
+
type: "pcb_board",
|
|
68
|
+
pcb_board_id: "board1",
|
|
69
|
+
center: { x: 50, y: 50 },
|
|
70
|
+
width: 100,
|
|
71
|
+
height: 100,
|
|
72
|
+
thickness: 1.6,
|
|
73
|
+
num_layers: 2,
|
|
74
|
+
material: "fr4",
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
drawer.setCameraBounds({ minX: 0, maxX: 100, minY: 0, maxY: 100 })
|
|
78
|
+
drawer.drawElements([board, hole])
|
|
55
79
|
|
|
56
80
|
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
57
81
|
import.meta.path,
|
|
@@ -81,7 +105,19 @@ test("draw pill plated hole", async () => {
|
|
|
81
105
|
ccw_rotation: 0,
|
|
82
106
|
}
|
|
83
107
|
|
|
84
|
-
|
|
108
|
+
const board: PcbBoard = {
|
|
109
|
+
type: "pcb_board",
|
|
110
|
+
pcb_board_id: "board1",
|
|
111
|
+
center: { x: 50, y: 50 },
|
|
112
|
+
width: 100,
|
|
113
|
+
height: 100,
|
|
114
|
+
thickness: 1.6,
|
|
115
|
+
num_layers: 2,
|
|
116
|
+
material: "fr4",
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
drawer.setCameraBounds({ minX: 0, maxX: 100, minY: 0, maxY: 100 })
|
|
120
|
+
drawer.drawElements([board, hole])
|
|
85
121
|
|
|
86
122
|
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
87
123
|
import.meta.path,
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
+
import type { AnyCircuitElement } from "circuit-json"
|
|
4
|
+
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
5
|
+
|
|
6
|
+
test("draw smt pads with asymmetric soldermask margins", async () => {
|
|
7
|
+
const canvas = createCanvas(800, 600)
|
|
8
|
+
const ctx = canvas.getContext("2d")
|
|
9
|
+
const drawer = new CircuitToCanvasDrawer(ctx)
|
|
10
|
+
|
|
11
|
+
ctx.fillStyle = "#1a1a1a"
|
|
12
|
+
ctx.fillRect(0, 0, 800, 600)
|
|
13
|
+
|
|
14
|
+
const circuit: AnyCircuitElement[] = [
|
|
15
|
+
{
|
|
16
|
+
type: "pcb_board",
|
|
17
|
+
pcb_board_id: "board0",
|
|
18
|
+
center: { x: 0, y: 0 },
|
|
19
|
+
width: 14,
|
|
20
|
+
height: 10,
|
|
21
|
+
thickness: 1.6,
|
|
22
|
+
num_layers: 2,
|
|
23
|
+
material: "fr4",
|
|
24
|
+
},
|
|
25
|
+
// Rectangle with asymmetric positive margins
|
|
26
|
+
{
|
|
27
|
+
type: "pcb_smtpad",
|
|
28
|
+
pcb_smtpad_id: "pad_rect_pos_asym",
|
|
29
|
+
shape: "rect",
|
|
30
|
+
layer: "top",
|
|
31
|
+
x: -4,
|
|
32
|
+
y: 2,
|
|
33
|
+
width: 2,
|
|
34
|
+
height: 1,
|
|
35
|
+
soldermask_margin_left: 0.5,
|
|
36
|
+
soldermask_margin_right: 0.1,
|
|
37
|
+
soldermask_margin_top: 0.2,
|
|
38
|
+
soldermask_margin_bottom: 0.8,
|
|
39
|
+
},
|
|
40
|
+
// Rectangle with asymmetric negative margins
|
|
41
|
+
{
|
|
42
|
+
type: "pcb_smtpad",
|
|
43
|
+
pcb_smtpad_id: "pad_rect_neg_asym",
|
|
44
|
+
shape: "rect",
|
|
45
|
+
layer: "top",
|
|
46
|
+
x: -4,
|
|
47
|
+
y: -2,
|
|
48
|
+
width: 2,
|
|
49
|
+
height: 1.5,
|
|
50
|
+
soldermask_margin_left: -0.2,
|
|
51
|
+
soldermask_margin_right: -0.6,
|
|
52
|
+
soldermask_margin_top: -0.1,
|
|
53
|
+
soldermask_margin_bottom: -0.4,
|
|
54
|
+
},
|
|
55
|
+
// Rotated rectangle with asymmetric positive margins
|
|
56
|
+
{
|
|
57
|
+
type: "pcb_smtpad",
|
|
58
|
+
pcb_smtpad_id: "pad_rot_rect_pos_asym",
|
|
59
|
+
shape: "rotated_rect",
|
|
60
|
+
layer: "top",
|
|
61
|
+
x: 4,
|
|
62
|
+
y: 2,
|
|
63
|
+
width: 2,
|
|
64
|
+
height: 1,
|
|
65
|
+
ccw_rotation: 45,
|
|
66
|
+
soldermask_margin_left: 0.8,
|
|
67
|
+
soldermask_margin_right: 0.2,
|
|
68
|
+
soldermask_margin_top: 0.1,
|
|
69
|
+
soldermask_margin_bottom: 0.5,
|
|
70
|
+
},
|
|
71
|
+
// Rotated rectangle with asymmetric negative margins
|
|
72
|
+
{
|
|
73
|
+
type: "pcb_smtpad",
|
|
74
|
+
pcb_smtpad_id: "pad_rot_rect_neg_asym",
|
|
75
|
+
shape: "rotated_rect",
|
|
76
|
+
layer: "top",
|
|
77
|
+
x: 4,
|
|
78
|
+
y: -2,
|
|
79
|
+
width: 2,
|
|
80
|
+
height: 1.5,
|
|
81
|
+
ccw_rotation: 30,
|
|
82
|
+
soldermask_margin_left: -0.5,
|
|
83
|
+
soldermask_margin_right: -0.1,
|
|
84
|
+
soldermask_margin_top: -0.4,
|
|
85
|
+
soldermask_margin_bottom: -0.2,
|
|
86
|
+
},
|
|
87
|
+
// Labels
|
|
88
|
+
{
|
|
89
|
+
type: "pcb_silkscreen_text",
|
|
90
|
+
pcb_silkscreen_text_id: "text1",
|
|
91
|
+
pcb_component_id: "comp1",
|
|
92
|
+
layer: "top",
|
|
93
|
+
anchor_position: { x: -4, y: 3.5 },
|
|
94
|
+
anchor_alignment: "center",
|
|
95
|
+
text: "Rect Pos Asym",
|
|
96
|
+
font_size: 0.4,
|
|
97
|
+
font: "tscircuit2024",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: "pcb_silkscreen_text",
|
|
101
|
+
pcb_silkscreen_text_id: "text2",
|
|
102
|
+
pcb_component_id: "comp1",
|
|
103
|
+
layer: "top",
|
|
104
|
+
anchor_position: { x: -4, y: -3.8 },
|
|
105
|
+
anchor_alignment: "center",
|
|
106
|
+
text: "Rect Neg Asym",
|
|
107
|
+
font_size: 0.4,
|
|
108
|
+
font: "tscircuit2024",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: "pcb_silkscreen_text",
|
|
112
|
+
pcb_silkscreen_text_id: "text3",
|
|
113
|
+
pcb_component_id: "comp1",
|
|
114
|
+
layer: "top",
|
|
115
|
+
anchor_position: { x: 4, y: 3.8 },
|
|
116
|
+
anchor_alignment: "center",
|
|
117
|
+
text: "Rot Rect Pos Asym",
|
|
118
|
+
font_size: 0.4,
|
|
119
|
+
font: "tscircuit2024",
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: "pcb_silkscreen_text",
|
|
123
|
+
pcb_silkscreen_text_id: "text4",
|
|
124
|
+
pcb_component_id: "comp1",
|
|
125
|
+
layer: "top",
|
|
126
|
+
anchor_position: { x: 4, y: -3.8 },
|
|
127
|
+
anchor_alignment: "center",
|
|
128
|
+
text: "Rot Rect Neg Asym",
|
|
129
|
+
font_size: 0.4,
|
|
130
|
+
font: "tscircuit2024",
|
|
131
|
+
},
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
drawer.setCameraBounds({ minX: -7, maxX: 7, minY: -5, maxY: 5 })
|
|
135
|
+
drawer.drawElements(circuit, { showSoldermask: true })
|
|
136
|
+
|
|
137
|
+
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
138
|
+
import.meta.path,
|
|
139
|
+
)
|
|
140
|
+
})
|
|
@@ -154,7 +154,7 @@ test("draw smt pads fully covered with soldermask and board with soldermask", as
|
|
|
154
154
|
]
|
|
155
155
|
|
|
156
156
|
drawer.setCameraBounds({ minX: -7, maxX: 7, minY: -5, maxY: 5 })
|
|
157
|
-
drawer.drawElements(circuit)
|
|
157
|
+
drawer.drawElements(circuit, { showSoldermask: true })
|
|
158
158
|
|
|
159
159
|
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
160
160
|
import.meta.path,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { expect, test } from "bun:test"
|
|
2
2
|
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
+
import type { AnyCircuitElement } from "circuit-json"
|
|
3
4
|
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
4
5
|
|
|
5
6
|
test("draw smt pads with positive and negative soldermask margins", async () => {
|
|
@@ -10,13 +11,16 @@ test("draw smt pads with positive and negative soldermask margins", async () =>
|
|
|
10
11
|
ctx.fillStyle = "#1a1a1a"
|
|
11
12
|
ctx.fillRect(0, 0, 800, 600)
|
|
12
13
|
|
|
13
|
-
const circuit:
|
|
14
|
+
const circuit: AnyCircuitElement[] = [
|
|
14
15
|
{
|
|
15
16
|
type: "pcb_board",
|
|
16
17
|
pcb_board_id: "board0",
|
|
17
18
|
center: { x: 0, y: 0 },
|
|
18
19
|
width: 14,
|
|
19
20
|
height: 10,
|
|
21
|
+
thickness: 1.6,
|
|
22
|
+
num_layers: 2,
|
|
23
|
+
material: "fr4",
|
|
20
24
|
},
|
|
21
25
|
// Rectangle with positive margin (mask extends beyond pad)
|
|
22
26
|
{
|
|
@@ -94,62 +98,74 @@ test("draw smt pads with positive and negative soldermask margins", async () =>
|
|
|
94
98
|
{
|
|
95
99
|
type: "pcb_silkscreen_text",
|
|
96
100
|
pcb_silkscreen_text_id: "text_rect_pos",
|
|
101
|
+
pcb_component_id: "comp1",
|
|
97
102
|
layer: "top",
|
|
98
103
|
anchor_position: { x: -4, y: 3.2 },
|
|
99
104
|
anchor_alignment: "center",
|
|
100
105
|
text: "+0.2mm",
|
|
101
106
|
font_size: 0.4,
|
|
107
|
+
font: "tscircuit2024",
|
|
102
108
|
},
|
|
103
109
|
{
|
|
104
110
|
type: "pcb_silkscreen_text",
|
|
105
111
|
pcb_silkscreen_text_id: "text_circle_pos",
|
|
112
|
+
pcb_component_id: "comp1",
|
|
106
113
|
layer: "top",
|
|
107
114
|
anchor_position: { x: 0, y: 3.2 },
|
|
108
115
|
anchor_alignment: "center",
|
|
109
116
|
text: "+0.15mm",
|
|
110
117
|
font_size: 0.4,
|
|
118
|
+
font: "tscircuit2024",
|
|
111
119
|
},
|
|
112
120
|
{
|
|
113
121
|
type: "pcb_silkscreen_text",
|
|
114
122
|
pcb_silkscreen_text_id: "text_pill_pos",
|
|
123
|
+
pcb_component_id: "comp1",
|
|
115
124
|
layer: "top",
|
|
116
125
|
anchor_position: { x: 4, y: 3.2 },
|
|
117
126
|
anchor_alignment: "center",
|
|
118
127
|
text: "+0.1mm",
|
|
119
128
|
font_size: 0.4,
|
|
129
|
+
font: "tscircuit2024",
|
|
120
130
|
},
|
|
121
131
|
// Silkscreen labels for negative margin pads (bottom row)
|
|
122
132
|
{
|
|
123
133
|
type: "pcb_silkscreen_text",
|
|
124
134
|
pcb_silkscreen_text_id: "text_rect_neg",
|
|
135
|
+
pcb_component_id: "comp1",
|
|
125
136
|
layer: "top",
|
|
126
137
|
anchor_position: { x: -4, y: -3.2 },
|
|
127
138
|
anchor_alignment: "center",
|
|
128
139
|
text: "-0.15mm",
|
|
129
140
|
font_size: 0.4,
|
|
141
|
+
font: "tscircuit2024",
|
|
130
142
|
},
|
|
131
143
|
{
|
|
132
144
|
type: "pcb_silkscreen_text",
|
|
133
145
|
pcb_silkscreen_text_id: "text_circle_neg",
|
|
146
|
+
pcb_component_id: "comp1",
|
|
134
147
|
layer: "top",
|
|
135
148
|
anchor_position: { x: 0, y: -3.2 },
|
|
136
149
|
anchor_alignment: "center",
|
|
137
150
|
text: "-0.2mm",
|
|
138
151
|
font_size: 0.4,
|
|
152
|
+
font: "tscircuit2024",
|
|
139
153
|
},
|
|
140
154
|
{
|
|
141
155
|
type: "pcb_silkscreen_text",
|
|
142
156
|
pcb_silkscreen_text_id: "text_pill_neg",
|
|
157
|
+
pcb_component_id: "comp1",
|
|
143
158
|
layer: "top",
|
|
144
159
|
anchor_position: { x: 4, y: -3.2 },
|
|
145
160
|
anchor_alignment: "center",
|
|
146
161
|
text: "-0.12mm",
|
|
147
162
|
font_size: 0.4,
|
|
163
|
+
font: "tscircuit2024",
|
|
148
164
|
},
|
|
149
165
|
]
|
|
150
166
|
|
|
151
167
|
drawer.setCameraBounds({ minX: -7, maxX: 7, minY: -5, maxY: 5 })
|
|
152
|
-
drawer.drawElements(circuit)
|
|
168
|
+
drawer.drawElements(circuit, { showSoldermask: true })
|
|
153
169
|
|
|
154
170
|
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
155
171
|
import.meta.path,
|
|
@@ -10,6 +10,7 @@ export interface StackedPngSvgComparisonOptions {
|
|
|
10
10
|
width?: number
|
|
11
11
|
height?: number
|
|
12
12
|
padding?: number
|
|
13
|
+
showSoldermask?: boolean
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -23,7 +24,12 @@ export async function getStackedPngSvgComparison(
|
|
|
23
24
|
circuitJson: AnyCircuitElement[],
|
|
24
25
|
options: StackedPngSvgComparisonOptions = {},
|
|
25
26
|
): Promise<Buffer> {
|
|
26
|
-
const {
|
|
27
|
+
const {
|
|
28
|
+
width = 400,
|
|
29
|
+
height = 800,
|
|
30
|
+
padding = 4,
|
|
31
|
+
showSoldermask = false,
|
|
32
|
+
} = options
|
|
27
33
|
|
|
28
34
|
const bounds = getBoundsOfPcbElements(circuitJson)
|
|
29
35
|
|
|
@@ -41,7 +47,7 @@ export async function getStackedPngSvgComparison(
|
|
|
41
47
|
minY: bounds.minY,
|
|
42
48
|
maxY: bounds.maxY,
|
|
43
49
|
})
|
|
44
|
-
drawer.drawElements(circuitJson)
|
|
50
|
+
drawer.drawElements(circuitJson, { showSoldermask })
|
|
45
51
|
|
|
46
52
|
const canvasPng = canvas.toBuffer("image/png")
|
|
47
53
|
|