circuit-to-canvas 0.0.36 → 0.0.38
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 +429 -275
- package/lib/drawer/CircuitToCanvasDrawer.ts +7 -1
- package/lib/drawer/elements/pcb-plated-hole.ts +203 -7
- package/lib/drawer/shapes/dimension-line.ts +74 -79
- package/package.json +1 -1
- package/tests/elements/__snapshots__/pcb-fabrication-note-dimension.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-plated-hole-soldermask-margin.snap.png +0 -0
- package/tests/elements/pcb-plated-hole-soldermask-margin.test.ts +225 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
+
import type { PcbPlatedHole } from "circuit-json"
|
|
4
|
+
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
5
|
+
|
|
6
|
+
test("draw plated holes with positive and negative 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: any = [
|
|
15
|
+
{
|
|
16
|
+
type: "pcb_board",
|
|
17
|
+
pcb_board_id: "board0",
|
|
18
|
+
center: { x: 0, y: 0 },
|
|
19
|
+
width: 16,
|
|
20
|
+
height: 14,
|
|
21
|
+
},
|
|
22
|
+
// Circle with positive margin (mask extends beyond pad)
|
|
23
|
+
{
|
|
24
|
+
type: "pcb_plated_hole",
|
|
25
|
+
pcb_plated_hole_id: "hole_circle_positive",
|
|
26
|
+
shape: "circle",
|
|
27
|
+
x: -5,
|
|
28
|
+
y: 3.5,
|
|
29
|
+
outer_diameter: 2,
|
|
30
|
+
hole_diameter: 1,
|
|
31
|
+
layers: ["top", "bottom"],
|
|
32
|
+
is_covered_with_solder_mask: true,
|
|
33
|
+
soldermask_margin: 0.2,
|
|
34
|
+
},
|
|
35
|
+
// Circle with negative margin (spacing around copper, copper visible)
|
|
36
|
+
{
|
|
37
|
+
type: "pcb_plated_hole",
|
|
38
|
+
pcb_plated_hole_id: "hole_circle_negative",
|
|
39
|
+
shape: "circle",
|
|
40
|
+
x: -5,
|
|
41
|
+
y: -3.5,
|
|
42
|
+
outer_diameter: 2,
|
|
43
|
+
hole_diameter: 1,
|
|
44
|
+
layers: ["top", "bottom"],
|
|
45
|
+
is_covered_with_solder_mask: true,
|
|
46
|
+
soldermask_margin: -0.15,
|
|
47
|
+
},
|
|
48
|
+
// Oval with positive margin
|
|
49
|
+
{
|
|
50
|
+
type: "pcb_plated_hole",
|
|
51
|
+
pcb_plated_hole_id: "hole_oval_positive",
|
|
52
|
+
shape: "oval",
|
|
53
|
+
x: 0,
|
|
54
|
+
y: 3.5,
|
|
55
|
+
outer_width: 2.4,
|
|
56
|
+
outer_height: 1.6,
|
|
57
|
+
hole_width: 1.6,
|
|
58
|
+
hole_height: 0.8,
|
|
59
|
+
layers: ["top", "bottom"],
|
|
60
|
+
ccw_rotation: 0,
|
|
61
|
+
is_covered_with_solder_mask: true,
|
|
62
|
+
soldermask_margin: 0.15,
|
|
63
|
+
},
|
|
64
|
+
// Oval with negative margin
|
|
65
|
+
{
|
|
66
|
+
type: "pcb_plated_hole",
|
|
67
|
+
pcb_plated_hole_id: "hole_oval_negative",
|
|
68
|
+
shape: "oval",
|
|
69
|
+
x: 0,
|
|
70
|
+
y: -3.5,
|
|
71
|
+
outer_width: 2.4,
|
|
72
|
+
outer_height: 1.6,
|
|
73
|
+
hole_width: 1.6,
|
|
74
|
+
hole_height: 0.8,
|
|
75
|
+
layers: ["top", "bottom"],
|
|
76
|
+
ccw_rotation: 0,
|
|
77
|
+
is_covered_with_solder_mask: true,
|
|
78
|
+
soldermask_margin: -0.2,
|
|
79
|
+
},
|
|
80
|
+
// Pill with positive margin
|
|
81
|
+
{
|
|
82
|
+
type: "pcb_plated_hole",
|
|
83
|
+
pcb_plated_hole_id: "hole_pill_positive",
|
|
84
|
+
shape: "pill",
|
|
85
|
+
x: 5,
|
|
86
|
+
y: 3.5,
|
|
87
|
+
outer_width: 3,
|
|
88
|
+
outer_height: 1.5,
|
|
89
|
+
hole_width: 2,
|
|
90
|
+
hole_height: 1,
|
|
91
|
+
layers: ["top", "bottom"],
|
|
92
|
+
ccw_rotation: 0,
|
|
93
|
+
is_covered_with_solder_mask: true,
|
|
94
|
+
soldermask_margin: 0.1,
|
|
95
|
+
},
|
|
96
|
+
// Pill with negative margin
|
|
97
|
+
{
|
|
98
|
+
type: "pcb_plated_hole",
|
|
99
|
+
pcb_plated_hole_id: "hole_pill_negative",
|
|
100
|
+
shape: "pill",
|
|
101
|
+
x: 5,
|
|
102
|
+
y: -3.5,
|
|
103
|
+
outer_width: 3,
|
|
104
|
+
outer_height: 1.5,
|
|
105
|
+
hole_width: 2,
|
|
106
|
+
hole_height: 1,
|
|
107
|
+
layers: ["top", "bottom"],
|
|
108
|
+
ccw_rotation: 0,
|
|
109
|
+
is_covered_with_solder_mask: true,
|
|
110
|
+
soldermask_margin: -0.12,
|
|
111
|
+
},
|
|
112
|
+
// Rectangular pad with circular hole - positive margin
|
|
113
|
+
{
|
|
114
|
+
type: "pcb_plated_hole",
|
|
115
|
+
pcb_plated_hole_id: "hole_rect_circle_positive",
|
|
116
|
+
shape: "circular_hole_with_rect_pad",
|
|
117
|
+
x: -2.5,
|
|
118
|
+
y: 0,
|
|
119
|
+
rect_pad_width: 2.4,
|
|
120
|
+
rect_pad_height: 1.6,
|
|
121
|
+
rect_border_radius: 0.2,
|
|
122
|
+
hole_diameter: 1,
|
|
123
|
+
layers: ["top", "bottom"],
|
|
124
|
+
is_covered_with_solder_mask: true,
|
|
125
|
+
soldermask_margin: 0.15,
|
|
126
|
+
},
|
|
127
|
+
// Rectangular pad with circular hole - negative margin
|
|
128
|
+
{
|
|
129
|
+
type: "pcb_plated_hole",
|
|
130
|
+
pcb_plated_hole_id: "hole_rect_circle_negative",
|
|
131
|
+
shape: "circular_hole_with_rect_pad",
|
|
132
|
+
x: 2.5,
|
|
133
|
+
y: 0,
|
|
134
|
+
rect_pad_width: 2.4,
|
|
135
|
+
rect_pad_height: 1.6,
|
|
136
|
+
rect_border_radius: 0.2,
|
|
137
|
+
hole_diameter: 1,
|
|
138
|
+
layers: ["top", "bottom"],
|
|
139
|
+
is_covered_with_solder_mask: true,
|
|
140
|
+
soldermask_margin: -0.1,
|
|
141
|
+
},
|
|
142
|
+
// Silkscreen labels for positive margin holes (top row)
|
|
143
|
+
{
|
|
144
|
+
type: "pcb_silkscreen_text",
|
|
145
|
+
pcb_silkscreen_text_id: "text_circle_pos",
|
|
146
|
+
layer: "top",
|
|
147
|
+
anchor_position: { x: -5, y: 5.2 },
|
|
148
|
+
anchor_alignment: "center",
|
|
149
|
+
text: "+0.2mm",
|
|
150
|
+
font_size: 0.4,
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
type: "pcb_silkscreen_text",
|
|
154
|
+
pcb_silkscreen_text_id: "text_oval_pos",
|
|
155
|
+
layer: "top",
|
|
156
|
+
anchor_position: { x: 0, y: 5.2 },
|
|
157
|
+
anchor_alignment: "center",
|
|
158
|
+
text: "+0.15mm",
|
|
159
|
+
font_size: 0.4,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
type: "pcb_silkscreen_text",
|
|
163
|
+
pcb_silkscreen_text_id: "text_pill_pos",
|
|
164
|
+
layer: "top",
|
|
165
|
+
anchor_position: { x: 5, y: 5.2 },
|
|
166
|
+
anchor_alignment: "center",
|
|
167
|
+
text: "+0.1mm",
|
|
168
|
+
font_size: 0.4,
|
|
169
|
+
},
|
|
170
|
+
// Silkscreen labels for middle row (rectangular pads)
|
|
171
|
+
{
|
|
172
|
+
type: "pcb_silkscreen_text",
|
|
173
|
+
pcb_silkscreen_text_id: "text_rect_pos",
|
|
174
|
+
layer: "top",
|
|
175
|
+
anchor_position: { x: -2.5, y: 1.8 },
|
|
176
|
+
anchor_alignment: "center",
|
|
177
|
+
text: "+0.15mm",
|
|
178
|
+
font_size: 0.4,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: "pcb_silkscreen_text",
|
|
182
|
+
pcb_silkscreen_text_id: "text_rect_neg",
|
|
183
|
+
layer: "top",
|
|
184
|
+
anchor_position: { x: 2.5, y: -1.8 },
|
|
185
|
+
anchor_alignment: "center",
|
|
186
|
+
text: "-0.1mm",
|
|
187
|
+
font_size: 0.4,
|
|
188
|
+
},
|
|
189
|
+
// Silkscreen labels for negative margin holes (bottom row)
|
|
190
|
+
{
|
|
191
|
+
type: "pcb_silkscreen_text",
|
|
192
|
+
pcb_silkscreen_text_id: "text_circle_neg",
|
|
193
|
+
layer: "top",
|
|
194
|
+
anchor_position: { x: -5, y: -5.2 },
|
|
195
|
+
anchor_alignment: "center",
|
|
196
|
+
text: "-0.15mm",
|
|
197
|
+
font_size: 0.4,
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: "pcb_silkscreen_text",
|
|
201
|
+
pcb_silkscreen_text_id: "text_oval_neg",
|
|
202
|
+
layer: "top",
|
|
203
|
+
anchor_position: { x: 0, y: -5.2 },
|
|
204
|
+
anchor_alignment: "center",
|
|
205
|
+
text: "-0.2mm",
|
|
206
|
+
font_size: 0.4,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
type: "pcb_silkscreen_text",
|
|
210
|
+
pcb_silkscreen_text_id: "text_pill_neg",
|
|
211
|
+
layer: "top",
|
|
212
|
+
anchor_position: { x: 5, y: -5.2 },
|
|
213
|
+
anchor_alignment: "center",
|
|
214
|
+
text: "-0.12mm",
|
|
215
|
+
font_size: 0.4,
|
|
216
|
+
},
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
drawer.setCameraBounds({ minX: -8, maxX: 8, minY: -6.5, maxY: 6.5 })
|
|
220
|
+
drawer.drawElements(circuit)
|
|
221
|
+
|
|
222
|
+
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
223
|
+
import.meta.path,
|
|
224
|
+
)
|
|
225
|
+
})
|