circuit-to-canvas 0.0.50 → 0.0.52
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 +10 -4
- package/dist/index.js +2911 -2719
- package/lib/drawer/CircuitToCanvasDrawer.ts +317 -366
- 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 +28 -13
- 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 +3 -292
- 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/package.json +1 -1
- 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-board.test.ts +2 -2
- 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-keepout-with-group-id.test.ts +1 -1
- 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 +1 -1
- package/tests/elements/pcb-smtpad-soldermask-coverage.test.ts +1 -1
- package/tests/elements/pcb-smtpad-soldermask-margin.test.ts +1 -1
- package/tests/fixtures/getStackedPngSvgComparison.ts +10 -4
|
@@ -5,13 +5,6 @@ import { drawCircle } from "../shapes/circle"
|
|
|
5
5
|
import { drawRect } from "../shapes/rect"
|
|
6
6
|
import { drawPill } from "../shapes/pill"
|
|
7
7
|
import { drawPolygon } from "../shapes/polygon"
|
|
8
|
-
import {
|
|
9
|
-
drawSoldermaskRingForRect,
|
|
10
|
-
drawSoldermaskRingForCircle,
|
|
11
|
-
drawSoldermaskRingForPill,
|
|
12
|
-
drawSoldermaskRingForPolygon,
|
|
13
|
-
offsetPolygonPoints,
|
|
14
|
-
} from "./soldermask-margin"
|
|
15
8
|
|
|
16
9
|
export interface DrawPcbSmtPadParams {
|
|
17
10
|
ctx: CanvasContext
|
|
@@ -27,68 +20,20 @@ function layerToColor(layer: string, colorMap: PcbColorMap): string {
|
|
|
27
20
|
)
|
|
28
21
|
}
|
|
29
22
|
|
|
30
|
-
function
|
|
31
|
-
return (
|
|
32
|
-
colorMap.soldermaskOverCopper[
|
|
33
|
-
layer as keyof typeof colorMap.soldermaskOverCopper
|
|
34
|
-
] ?? colorMap.soldermaskOverCopper.top
|
|
35
|
-
)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function getBorderRadius(pad: PcbSmtPad, margin = 0): number {
|
|
39
|
-
let r = 0
|
|
23
|
+
function getBorderRadius(pad: PcbSmtPad): number {
|
|
40
24
|
if (pad.shape === "rect" || pad.shape === "rotated_rect") {
|
|
41
|
-
|
|
25
|
+
return pad.corner_radius ?? pad.rect_border_radius ?? 0
|
|
42
26
|
}
|
|
43
|
-
return
|
|
27
|
+
return 0
|
|
44
28
|
}
|
|
45
29
|
|
|
46
30
|
export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
47
31
|
const { ctx, pad, realToCanvasMat, colorMap } = params
|
|
48
32
|
|
|
49
33
|
const color = layerToColor(pad.layer, colorMap)
|
|
50
|
-
const isCoveredWithSoldermask = pad.is_covered_with_solder_mask === true
|
|
51
|
-
const margin = isCoveredWithSoldermask ? 0 : (pad.soldermask_margin ?? 0)
|
|
52
|
-
|
|
53
|
-
const soldermaskRingColor = getSoldermaskColor(pad.layer, colorMap)
|
|
54
|
-
const positiveMarginColor = colorMap.substrate
|
|
55
|
-
const soldermaskOverlayColor = getSoldermaskColor(pad.layer, colorMap)
|
|
56
|
-
|
|
57
|
-
const hasSoldermask = !isCoveredWithSoldermask && margin !== 0
|
|
58
|
-
|
|
59
|
-
let ml = margin
|
|
60
|
-
let mr = margin
|
|
61
|
-
let mt = margin
|
|
62
|
-
let mb = margin
|
|
63
|
-
let hasAnySoldermask = hasSoldermask
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
!isCoveredWithSoldermask &&
|
|
67
|
-
(pad.shape === "rect" || pad.shape === "rotated_rect")
|
|
68
|
-
) {
|
|
69
|
-
ml = pad.soldermask_margin_left ?? pad.soldermask_margin ?? 0
|
|
70
|
-
mr = pad.soldermask_margin_right ?? pad.soldermask_margin ?? 0
|
|
71
|
-
mt = pad.soldermask_margin_top ?? pad.soldermask_margin ?? 0
|
|
72
|
-
mb = pad.soldermask_margin_bottom ?? pad.soldermask_margin ?? 0
|
|
73
|
-
hasAnySoldermask = ml !== 0 || mr !== 0 || mt !== 0 || mb !== 0
|
|
74
|
-
}
|
|
75
34
|
|
|
76
35
|
// Draw the copper pad
|
|
77
36
|
if (pad.shape === "rect") {
|
|
78
|
-
// For positive margins, draw extended mask area first
|
|
79
|
-
if (hasAnySoldermask && (ml > 0 || mr > 0 || mt > 0 || mb > 0)) {
|
|
80
|
-
drawRect({
|
|
81
|
-
ctx,
|
|
82
|
-
center: { x: pad.x + (mr - ml) / 2, y: pad.y + (mt - mb) / 2 },
|
|
83
|
-
width: pad.width + ml + mr,
|
|
84
|
-
height: pad.height + mt + mb,
|
|
85
|
-
fill: positiveMarginColor,
|
|
86
|
-
realToCanvasMat,
|
|
87
|
-
borderRadius: getBorderRadius(pad),
|
|
88
|
-
})
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Draw the pad on top
|
|
92
37
|
drawRect({
|
|
93
38
|
ctx,
|
|
94
39
|
center: { x: pad.x, y: pad.y },
|
|
@@ -98,61 +43,10 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
98
43
|
realToCanvasMat,
|
|
99
44
|
borderRadius: getBorderRadius(pad),
|
|
100
45
|
})
|
|
101
|
-
|
|
102
|
-
// For negative margins, draw soldermask ring on top of the pad
|
|
103
|
-
if (hasAnySoldermask && (ml < 0 || mr < 0 || mt < 0 || mb < 0)) {
|
|
104
|
-
drawSoldermaskRingForRect(
|
|
105
|
-
ctx,
|
|
106
|
-
{ x: pad.x, y: pad.y },
|
|
107
|
-
pad.width,
|
|
108
|
-
pad.height,
|
|
109
|
-
pad.soldermask_margin ?? 0,
|
|
110
|
-
getBorderRadius(pad),
|
|
111
|
-
0,
|
|
112
|
-
realToCanvasMat,
|
|
113
|
-
soldermaskRingColor,
|
|
114
|
-
color,
|
|
115
|
-
{ left: ml, right: mr, top: mt, bottom: mb },
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// If covered with soldermask, draw soldermaskOverCopper overlay
|
|
120
|
-
if (isCoveredWithSoldermask) {
|
|
121
|
-
drawRect({
|
|
122
|
-
ctx,
|
|
123
|
-
center: { x: pad.x, y: pad.y },
|
|
124
|
-
width: pad.width,
|
|
125
|
-
height: pad.height,
|
|
126
|
-
fill: soldermaskOverlayColor,
|
|
127
|
-
realToCanvasMat,
|
|
128
|
-
borderRadius: getBorderRadius(pad),
|
|
129
|
-
})
|
|
130
|
-
}
|
|
131
46
|
return
|
|
132
47
|
}
|
|
133
48
|
|
|
134
49
|
if (pad.shape === "rotated_rect") {
|
|
135
|
-
const radians = ((pad.ccw_rotation ?? 0) * Math.PI) / 180
|
|
136
|
-
const dxLocal = (mr - ml) / 2
|
|
137
|
-
const dyLocal = (mt - mb) / 2
|
|
138
|
-
const dxGlobal = dxLocal * Math.cos(radians) - dyLocal * Math.sin(radians)
|
|
139
|
-
const dyGlobal = dxLocal * Math.sin(radians) + dyLocal * Math.cos(radians)
|
|
140
|
-
|
|
141
|
-
// For positive margins, draw extended mask area first
|
|
142
|
-
if (hasAnySoldermask && (ml > 0 || mr > 0 || mt > 0 || mb > 0)) {
|
|
143
|
-
drawRect({
|
|
144
|
-
ctx,
|
|
145
|
-
center: { x: pad.x + dxGlobal, y: pad.y + dyGlobal },
|
|
146
|
-
width: pad.width + ml + mr,
|
|
147
|
-
height: pad.height + mt + mb,
|
|
148
|
-
fill: positiveMarginColor,
|
|
149
|
-
realToCanvasMat,
|
|
150
|
-
borderRadius: getBorderRadius(pad),
|
|
151
|
-
rotation: pad.ccw_rotation ?? 0,
|
|
152
|
-
})
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Draw the pad on top
|
|
156
50
|
drawRect({
|
|
157
51
|
ctx,
|
|
158
52
|
center: { x: pad.x, y: pad.y },
|
|
@@ -163,53 +57,10 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
163
57
|
borderRadius: getBorderRadius(pad),
|
|
164
58
|
rotation: pad.ccw_rotation ?? 0,
|
|
165
59
|
})
|
|
166
|
-
|
|
167
|
-
// For negative margins, draw soldermask ring on top of the pad
|
|
168
|
-
if (hasAnySoldermask && (ml < 0 || mr < 0 || mt < 0 || mb < 0)) {
|
|
169
|
-
drawSoldermaskRingForRect(
|
|
170
|
-
ctx,
|
|
171
|
-
{ x: pad.x, y: pad.y },
|
|
172
|
-
pad.width,
|
|
173
|
-
pad.height,
|
|
174
|
-
pad.soldermask_margin ?? 0,
|
|
175
|
-
getBorderRadius(pad),
|
|
176
|
-
pad.ccw_rotation ?? 0,
|
|
177
|
-
realToCanvasMat,
|
|
178
|
-
soldermaskRingColor,
|
|
179
|
-
color,
|
|
180
|
-
{ left: ml, right: mr, top: mt, bottom: mb },
|
|
181
|
-
)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// If covered with soldermask, draw soldermaskOverCopper overlay
|
|
185
|
-
if (isCoveredWithSoldermask) {
|
|
186
|
-
drawRect({
|
|
187
|
-
ctx,
|
|
188
|
-
center: { x: pad.x, y: pad.y },
|
|
189
|
-
width: pad.width,
|
|
190
|
-
height: pad.height,
|
|
191
|
-
fill: soldermaskOverlayColor,
|
|
192
|
-
realToCanvasMat,
|
|
193
|
-
borderRadius: getBorderRadius(pad),
|
|
194
|
-
rotation: pad.ccw_rotation ?? 0,
|
|
195
|
-
})
|
|
196
|
-
}
|
|
197
60
|
return
|
|
198
61
|
}
|
|
199
62
|
|
|
200
63
|
if (pad.shape === "circle") {
|
|
201
|
-
// For positive margins, draw extended mask area first
|
|
202
|
-
if (hasSoldermask && margin > 0) {
|
|
203
|
-
drawCircle({
|
|
204
|
-
ctx,
|
|
205
|
-
center: { x: pad.x, y: pad.y },
|
|
206
|
-
radius: pad.radius + margin,
|
|
207
|
-
fill: positiveMarginColor,
|
|
208
|
-
realToCanvasMat,
|
|
209
|
-
})
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Draw the pad on top
|
|
213
64
|
drawCircle({
|
|
214
65
|
ctx,
|
|
215
66
|
center: { x: pad.x, y: pad.y },
|
|
@@ -217,47 +68,10 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
217
68
|
fill: color,
|
|
218
69
|
realToCanvasMat,
|
|
219
70
|
})
|
|
220
|
-
|
|
221
|
-
// For negative margins, draw soldermask ring on top of the pad
|
|
222
|
-
if (hasSoldermask && margin < 0) {
|
|
223
|
-
drawSoldermaskRingForCircle(
|
|
224
|
-
ctx,
|
|
225
|
-
{ x: pad.x, y: pad.y },
|
|
226
|
-
pad.radius,
|
|
227
|
-
margin,
|
|
228
|
-
realToCanvasMat,
|
|
229
|
-
soldermaskRingColor,
|
|
230
|
-
color,
|
|
231
|
-
)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// If covered with soldermask and margin == 0 (treat as 0 positive margin), draw soldermaskOverCopper overlay
|
|
235
|
-
if (isCoveredWithSoldermask && margin === 0) {
|
|
236
|
-
drawCircle({
|
|
237
|
-
ctx,
|
|
238
|
-
center: { x: pad.x, y: pad.y },
|
|
239
|
-
radius: pad.radius,
|
|
240
|
-
fill: soldermaskOverlayColor,
|
|
241
|
-
realToCanvasMat,
|
|
242
|
-
})
|
|
243
|
-
}
|
|
244
71
|
return
|
|
245
72
|
}
|
|
246
73
|
|
|
247
74
|
if (pad.shape === "pill") {
|
|
248
|
-
// For positive margins, draw extended mask area first
|
|
249
|
-
if (hasSoldermask && margin > 0) {
|
|
250
|
-
drawPill({
|
|
251
|
-
ctx,
|
|
252
|
-
center: { x: pad.x, y: pad.y },
|
|
253
|
-
width: pad.width + margin * 2,
|
|
254
|
-
height: pad.height + margin * 2,
|
|
255
|
-
fill: positiveMarginColor,
|
|
256
|
-
realToCanvasMat,
|
|
257
|
-
})
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Draw the pad on top
|
|
261
75
|
drawPill({
|
|
262
76
|
ctx,
|
|
263
77
|
center: { x: pad.x, y: pad.y },
|
|
@@ -266,51 +80,10 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
266
80
|
fill: color,
|
|
267
81
|
realToCanvasMat,
|
|
268
82
|
})
|
|
269
|
-
|
|
270
|
-
// For negative margins, draw soldermask ring on top of the pad
|
|
271
|
-
if (hasSoldermask && margin < 0) {
|
|
272
|
-
drawSoldermaskRingForPill(
|
|
273
|
-
ctx,
|
|
274
|
-
{ x: pad.x, y: pad.y },
|
|
275
|
-
pad.width,
|
|
276
|
-
pad.height,
|
|
277
|
-
margin,
|
|
278
|
-
0,
|
|
279
|
-
realToCanvasMat,
|
|
280
|
-
soldermaskRingColor,
|
|
281
|
-
color,
|
|
282
|
-
)
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// If covered with soldermask and margin == 0 (treat as 0 positive margin), draw soldermaskOverCopper overlay
|
|
286
|
-
if (isCoveredWithSoldermask && margin === 0) {
|
|
287
|
-
drawPill({
|
|
288
|
-
ctx,
|
|
289
|
-
center: { x: pad.x, y: pad.y },
|
|
290
|
-
width: pad.width,
|
|
291
|
-
height: pad.height,
|
|
292
|
-
fill: soldermaskOverlayColor,
|
|
293
|
-
realToCanvasMat,
|
|
294
|
-
})
|
|
295
|
-
}
|
|
296
83
|
return
|
|
297
84
|
}
|
|
298
85
|
|
|
299
86
|
if (pad.shape === "rotated_pill") {
|
|
300
|
-
// For positive margins, draw extended mask area first
|
|
301
|
-
if (hasSoldermask && margin > 0) {
|
|
302
|
-
drawPill({
|
|
303
|
-
ctx,
|
|
304
|
-
center: { x: pad.x, y: pad.y },
|
|
305
|
-
width: pad.width + margin * 2,
|
|
306
|
-
height: pad.height + margin * 2,
|
|
307
|
-
fill: positiveMarginColor,
|
|
308
|
-
realToCanvasMat,
|
|
309
|
-
rotation: pad.ccw_rotation ?? 0,
|
|
310
|
-
})
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Draw the pad on top
|
|
314
87
|
drawPill({
|
|
315
88
|
ctx,
|
|
316
89
|
center: { x: pad.x, y: pad.y },
|
|
@@ -320,79 +93,17 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
320
93
|
realToCanvasMat,
|
|
321
94
|
rotation: pad.ccw_rotation ?? 0,
|
|
322
95
|
})
|
|
323
|
-
|
|
324
|
-
// For negative margins, draw soldermask ring on top of the pad
|
|
325
|
-
if (hasSoldermask && margin < 0) {
|
|
326
|
-
drawSoldermaskRingForPill(
|
|
327
|
-
ctx,
|
|
328
|
-
{ x: pad.x, y: pad.y },
|
|
329
|
-
pad.width,
|
|
330
|
-
pad.height,
|
|
331
|
-
margin,
|
|
332
|
-
pad.ccw_rotation ?? 0,
|
|
333
|
-
realToCanvasMat,
|
|
334
|
-
soldermaskRingColor,
|
|
335
|
-
color,
|
|
336
|
-
)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// If covered with soldermask and margin == 0 (treat as 0 positive margin), draw soldermaskOverCopper overlay
|
|
340
|
-
if (isCoveredWithSoldermask && margin === 0) {
|
|
341
|
-
drawPill({
|
|
342
|
-
ctx,
|
|
343
|
-
center: { x: pad.x, y: pad.y },
|
|
344
|
-
width: pad.width,
|
|
345
|
-
height: pad.height,
|
|
346
|
-
fill: soldermaskOverlayColor,
|
|
347
|
-
realToCanvasMat,
|
|
348
|
-
rotation: pad.ccw_rotation ?? 0,
|
|
349
|
-
})
|
|
350
|
-
}
|
|
351
96
|
return
|
|
352
97
|
}
|
|
353
98
|
|
|
354
99
|
if (pad.shape === "polygon") {
|
|
355
100
|
if (pad.points && pad.points.length >= 3) {
|
|
356
|
-
// For positive margins, draw extended mask area first
|
|
357
|
-
if (hasSoldermask && margin > 0) {
|
|
358
|
-
const expandedPoints = offsetPolygonPoints(pad.points, margin)
|
|
359
|
-
drawPolygon({
|
|
360
|
-
ctx,
|
|
361
|
-
points: expandedPoints,
|
|
362
|
-
fill: positiveMarginColor,
|
|
363
|
-
realToCanvasMat,
|
|
364
|
-
})
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Draw the copper pad
|
|
368
101
|
drawPolygon({
|
|
369
102
|
ctx,
|
|
370
103
|
points: pad.points,
|
|
371
104
|
fill: color,
|
|
372
105
|
realToCanvasMat,
|
|
373
106
|
})
|
|
374
|
-
|
|
375
|
-
// For negative margins, draw soldermask ring on top of the pad
|
|
376
|
-
if (hasSoldermask && margin < 0) {
|
|
377
|
-
drawSoldermaskRingForPolygon(
|
|
378
|
-
ctx,
|
|
379
|
-
pad.points,
|
|
380
|
-
margin,
|
|
381
|
-
realToCanvasMat,
|
|
382
|
-
soldermaskRingColor,
|
|
383
|
-
color,
|
|
384
|
-
)
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
// If covered with soldermask and margin == 0 (treat as 0 positive margin), draw soldermaskOverCopper overlay
|
|
388
|
-
if (isCoveredWithSoldermask && margin === 0) {
|
|
389
|
-
drawPolygon({
|
|
390
|
-
ctx,
|
|
391
|
-
points: pad.points,
|
|
392
|
-
fill: soldermaskOverlayColor,
|
|
393
|
-
realToCanvasMat,
|
|
394
|
-
})
|
|
395
|
-
}
|
|
396
107
|
}
|
|
397
108
|
return
|
|
398
109
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PcbBoard } from "circuit-json"
|
|
2
|
+
import type { Matrix } from "transformation-matrix"
|
|
3
|
+
import { applyToPoint } from "transformation-matrix"
|
|
4
|
+
import type { CanvasContext } from "../../types"
|
|
5
|
+
import { drawPolygonPath } from "../helper-functions/draw-polygon"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Draws the base soldermask layer covering the entire board.
|
|
9
|
+
*/
|
|
10
|
+
export function drawBoardSoldermask(params: {
|
|
11
|
+
ctx: CanvasContext
|
|
12
|
+
board: PcbBoard
|
|
13
|
+
realToCanvasMat: Matrix
|
|
14
|
+
soldermaskColor: string
|
|
15
|
+
}): void {
|
|
16
|
+
const { ctx, board, realToCanvasMat, soldermaskColor } = params
|
|
17
|
+
const { width, height, center, outline } = board
|
|
18
|
+
|
|
19
|
+
if (outline && Array.isArray(outline) && outline.length >= 3) {
|
|
20
|
+
// Draw filled polygon for custom outline
|
|
21
|
+
const canvasPoints = outline.map((p) => {
|
|
22
|
+
const [x, y] = applyToPoint(realToCanvasMat, [p.x, p.y])
|
|
23
|
+
return { x, y }
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
ctx.beginPath()
|
|
27
|
+
drawPolygonPath({ ctx, points: canvasPoints })
|
|
28
|
+
ctx.fillStyle = soldermaskColor
|
|
29
|
+
ctx.fill()
|
|
30
|
+
} else if (width !== undefined && height !== undefined && center) {
|
|
31
|
+
// Draw filled rectangle
|
|
32
|
+
const [cx, cy] = applyToPoint(realToCanvasMat, [center.x, center.y])
|
|
33
|
+
const scaledWidth = width * Math.abs(realToCanvasMat.a)
|
|
34
|
+
const scaledHeight = height * Math.abs(realToCanvasMat.a)
|
|
35
|
+
|
|
36
|
+
ctx.fillStyle = soldermaskColor
|
|
37
|
+
ctx.fillRect(
|
|
38
|
+
cx - scaledWidth / 2,
|
|
39
|
+
cy - scaledHeight / 2,
|
|
40
|
+
scaledWidth,
|
|
41
|
+
scaledHeight,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { PcbCutout } from "circuit-json"
|
|
2
|
+
import type { Matrix } from "transformation-matrix"
|
|
3
|
+
import { applyToPoint } from "transformation-matrix"
|
|
4
|
+
import type { CanvasContext, PcbColorMap } from "../../types"
|
|
5
|
+
import { drawPolygonPath } from "../helper-functions/draw-polygon"
|
|
6
|
+
import { drawRoundedRectPath } from "../helper-functions/draw-rounded-rect"
|
|
7
|
+
/**
|
|
8
|
+
* Process soldermask for a board cutout.
|
|
9
|
+
* Cutouts go through the entire board, so they cut through soldermask too.
|
|
10
|
+
*/
|
|
11
|
+
export function processCutoutSoldermask(params: {
|
|
12
|
+
ctx: CanvasContext
|
|
13
|
+
cutout: PcbCutout
|
|
14
|
+
realToCanvasMat: Matrix
|
|
15
|
+
colorMap: PcbColorMap
|
|
16
|
+
}): void {
|
|
17
|
+
const { ctx, cutout, realToCanvasMat, colorMap } = params
|
|
18
|
+
// Cutouts go through the entire board, so they cut through soldermask too
|
|
19
|
+
// Use drill color to indicate the cutout
|
|
20
|
+
ctx.fillStyle = colorMap.drill
|
|
21
|
+
|
|
22
|
+
if (cutout.shape === "rect") {
|
|
23
|
+
const [cx, cy] = applyToPoint(realToCanvasMat, [
|
|
24
|
+
cutout.center?.x ?? 0,
|
|
25
|
+
cutout.center?.y ?? 0,
|
|
26
|
+
])
|
|
27
|
+
const scaledWidth = cutout.width * Math.abs(realToCanvasMat.a)
|
|
28
|
+
const scaledHeight = cutout.height * Math.abs(realToCanvasMat.a)
|
|
29
|
+
const scaledRadius =
|
|
30
|
+
(cutout.corner_radius ?? 0) * Math.abs(realToCanvasMat.a)
|
|
31
|
+
|
|
32
|
+
ctx.save()
|
|
33
|
+
ctx.translate(cx, cy)
|
|
34
|
+
if (cutout.rotation) {
|
|
35
|
+
ctx.rotate((-cutout.rotation * Math.PI) / 180)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
ctx.beginPath()
|
|
39
|
+
drawRoundedRectPath({
|
|
40
|
+
ctx,
|
|
41
|
+
cx: 0,
|
|
42
|
+
cy: 0,
|
|
43
|
+
width: scaledWidth,
|
|
44
|
+
height: scaledHeight,
|
|
45
|
+
radius: scaledRadius,
|
|
46
|
+
})
|
|
47
|
+
ctx.restore()
|
|
48
|
+
ctx.fill()
|
|
49
|
+
} else if (cutout.shape === "circle") {
|
|
50
|
+
const [cx, cy] = applyToPoint(realToCanvasMat, [
|
|
51
|
+
cutout.center?.x ?? 0,
|
|
52
|
+
cutout.center?.y ?? 0,
|
|
53
|
+
])
|
|
54
|
+
const scaledRadius = cutout.radius * Math.abs(realToCanvasMat.a)
|
|
55
|
+
|
|
56
|
+
ctx.beginPath()
|
|
57
|
+
ctx.arc(cx, cy, scaledRadius, 0, Math.PI * 2)
|
|
58
|
+
ctx.closePath()
|
|
59
|
+
ctx.fill()
|
|
60
|
+
} else if (
|
|
61
|
+
cutout.shape === "polygon" &&
|
|
62
|
+
cutout.points &&
|
|
63
|
+
cutout.points.length >= 3
|
|
64
|
+
) {
|
|
65
|
+
const canvasPoints = cutout.points.map((p) => {
|
|
66
|
+
const [x, y] = applyToPoint(realToCanvasMat, [p.x, p.y])
|
|
67
|
+
return { x, y }
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
ctx.beginPath()
|
|
71
|
+
drawPolygonPath({ ctx, points: canvasPoints })
|
|
72
|
+
ctx.fill()
|
|
73
|
+
}
|
|
74
|
+
}
|