circuit-to-canvas 0.0.45 → 0.0.46

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.
Files changed (26) hide show
  1. package/dist/index.js +394 -157
  2. package/lib/drawer/CircuitToCanvasDrawer.ts +1 -5
  3. package/lib/drawer/elements/pcb-hole.ts +248 -61
  4. package/lib/drawer/elements/pcb-plated-hole.ts +194 -102
  5. package/lib/drawer/elements/pcb-smtpad.ts +14 -17
  6. package/package.json +1 -1
  7. package/tests/board-snapshot/__snapshots__/usb-c-flashlight-board.snap.png +0 -0
  8. package/tests/elements/__snapshots__/board-with-elements.snap.png +0 -0
  9. package/tests/elements/__snapshots__/custom-outline-board.snap.png +0 -0
  10. package/tests/elements/__snapshots__/pcb-board.snap.png +0 -0
  11. package/tests/elements/__snapshots__/pcb-comprehensive-soldermask-margin.snap.png +0 -0
  12. package/tests/elements/__snapshots__/pcb-fabrication-note-dimension.snap.png +0 -0
  13. package/tests/elements/__snapshots__/pcb-hole-soldermask-margin.snap.png +0 -0
  14. package/tests/elements/__snapshots__/pcb-keepout-layer-filter.snap.png +0 -0
  15. package/tests/elements/__snapshots__/pcb-keepout-multiple-layers.snap.png +0 -0
  16. package/tests/elements/__snapshots__/pcb-keepout-rect-and-circle.snap.png +0 -0
  17. package/tests/elements/__snapshots__/pcb-keepout-with-group-id.snap.png +0 -0
  18. package/tests/elements/__snapshots__/pcb-note-dimension-with-offset.snap.png +0 -0
  19. package/tests/elements/__snapshots__/pcb-plated-hole-soldermask-margin.snap.png +0 -0
  20. package/tests/elements/__snapshots__/pcb-silkscreen-oval.snap.png +0 -0
  21. package/tests/elements/__snapshots__/pcb-smtpad-soldermask-margin.snap.png +0 -0
  22. package/tests/elements/pcb-comprehensive-soldermask-margin.test.ts +330 -0
  23. package/tests/elements/pcb-hole-soldermask-margin.test.ts +5 -5
  24. package/tests/elements/pcb-plated-hole-soldermask-margin.test.ts +8 -8
  25. package/tests/elements/pcb-smtpad-soldermask-margin.test.ts +0 -6
  26. package/tests/shapes/__snapshots__/dimension-line.snap.png +0 -0
@@ -35,11 +35,12 @@ function getSoldermaskColor(
35
35
  export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
36
36
  const { ctx, hole, realToCanvasMat, colorMap } = params
37
37
 
38
+ const isCoveredWithSoldermask = hole.is_covered_with_solder_mask === true
39
+ const margin = isCoveredWithSoldermask ? 0 : (hole.soldermask_margin ?? 0)
38
40
  const hasSoldermask =
39
- hole.is_covered_with_solder_mask === true &&
41
+ !isCoveredWithSoldermask &&
40
42
  hole.soldermask_margin !== undefined &&
41
43
  hole.soldermask_margin !== 0
42
- const margin = hasSoldermask ? hole.soldermask_margin! : 0
43
44
  const soldermaskRingColor = getSoldermaskColor(hole.layers, colorMap)
44
45
  const positiveMarginColor = colorMap.substrate
45
46
  const copperColor = colorMap.copper.top
@@ -78,14 +79,27 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
78
79
  )
79
80
  }
80
81
 
81
- // Draw inner drill hole
82
- drawCircle({
83
- ctx,
84
- center: { x: hole.x, y: hole.y },
85
- radius: hole.hole_diameter / 2,
86
- fill: colorMap.drill,
87
- realToCanvasMat,
88
- })
82
+ // If fully covered, draw soldermask overlay
83
+ if (isCoveredWithSoldermask) {
84
+ drawCircle({
85
+ ctx,
86
+ center: { x: hole.x, y: hole.y },
87
+ radius: hole.outer_diameter / 2,
88
+ fill: soldermaskRingColor,
89
+ realToCanvasMat,
90
+ })
91
+ }
92
+
93
+ // Draw inner drill hole (only if not fully covered with soldermask)
94
+ if (!isCoveredWithSoldermask) {
95
+ drawCircle({
96
+ ctx,
97
+ center: { x: hole.x, y: hole.y },
98
+ radius: hole.hole_diameter / 2,
99
+ fill: colorMap.drill,
100
+ realToCanvasMat,
101
+ })
102
+ }
89
103
  return
90
104
  }
91
105
 
@@ -129,16 +143,31 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
129
143
  )
130
144
  }
131
145
 
132
- // Draw inner drill hole
133
- drawOval({
134
- ctx,
135
- center: { x: hole.x, y: hole.y },
136
- radius_x: hole.hole_width / 2,
137
- radius_y: hole.hole_height / 2,
138
- fill: colorMap.drill,
139
- realToCanvasMat,
140
- rotation: hole.ccw_rotation,
141
- })
146
+ // If fully covered, draw soldermask overlay
147
+ if (isCoveredWithSoldermask) {
148
+ drawOval({
149
+ ctx,
150
+ center: { x: hole.x, y: hole.y },
151
+ radius_x: hole.outer_width / 2,
152
+ radius_y: hole.outer_height / 2,
153
+ fill: soldermaskRingColor,
154
+ realToCanvasMat,
155
+ rotation: hole.ccw_rotation,
156
+ })
157
+ }
158
+
159
+ // Draw inner drill hole (only if not fully covered with soldermask)
160
+ if (!isCoveredWithSoldermask) {
161
+ drawOval({
162
+ ctx,
163
+ center: { x: hole.x, y: hole.y },
164
+ radius_x: hole.hole_width / 2,
165
+ radius_y: hole.hole_height / 2,
166
+ fill: colorMap.drill,
167
+ realToCanvasMat,
168
+ rotation: hole.ccw_rotation,
169
+ })
170
+ }
142
171
  return
143
172
  }
144
173
 
@@ -182,16 +211,31 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
182
211
  )
183
212
  }
184
213
 
185
- // Draw inner drill hole
186
- drawPill({
187
- ctx,
188
- center: { x: hole.x, y: hole.y },
189
- width: hole.hole_width,
190
- height: hole.hole_height,
191
- fill: colorMap.drill,
192
- realToCanvasMat,
193
- rotation: hole.ccw_rotation,
194
- })
214
+ // If fully covered, draw soldermask overlay
215
+ if (isCoveredWithSoldermask) {
216
+ drawPill({
217
+ ctx,
218
+ center: { x: hole.x, y: hole.y },
219
+ width: hole.outer_width,
220
+ height: hole.outer_height,
221
+ fill: soldermaskRingColor,
222
+ realToCanvasMat,
223
+ rotation: hole.ccw_rotation,
224
+ })
225
+ }
226
+
227
+ // Draw inner drill hole (only if not fully covered with soldermask)
228
+ if (!isCoveredWithSoldermask) {
229
+ drawPill({
230
+ ctx,
231
+ center: { x: hole.x, y: hole.y },
232
+ width: hole.hole_width,
233
+ height: hole.hole_height,
234
+ fill: colorMap.drill,
235
+ realToCanvasMat,
236
+ rotation: hole.ccw_rotation,
237
+ })
238
+ }
195
239
  return
196
240
  }
197
241
 
@@ -236,16 +280,31 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
236
280
  )
237
281
  }
238
282
 
239
- // Draw circular drill hole (with offset)
240
- const holeX = hole.x + (hole.hole_offset_x ?? 0)
241
- const holeY = hole.y + (hole.hole_offset_y ?? 0)
242
- drawCircle({
243
- ctx,
244
- center: { x: holeX, y: holeY },
245
- radius: hole.hole_diameter / 2,
246
- fill: colorMap.drill,
247
- realToCanvasMat,
248
- })
283
+ // If fully covered, draw soldermask overlay
284
+ if (isCoveredWithSoldermask) {
285
+ drawRect({
286
+ ctx,
287
+ center: { x: hole.x, y: hole.y },
288
+ width: hole.rect_pad_width,
289
+ height: hole.rect_pad_height,
290
+ fill: soldermaskRingColor,
291
+ realToCanvasMat,
292
+ borderRadius: hole.rect_border_radius ?? 0,
293
+ })
294
+ }
295
+
296
+ // Draw circular drill hole (with offset, only if not fully covered with soldermask)
297
+ if (!isCoveredWithSoldermask) {
298
+ const holeX = hole.x + (hole.hole_offset_x ?? 0)
299
+ const holeY = hole.y + (hole.hole_offset_y ?? 0)
300
+ drawCircle({
301
+ ctx,
302
+ center: { x: holeX, y: holeY },
303
+ radius: hole.hole_diameter / 2,
304
+ fill: colorMap.drill,
305
+ realToCanvasMat,
306
+ })
307
+ }
249
308
  return
250
309
  }
251
310
 
@@ -290,17 +349,32 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
290
349
  )
291
350
  }
292
351
 
293
- // Draw pill drill hole (with offset)
294
- const holeX = hole.x + (hole.hole_offset_x ?? 0)
295
- const holeY = hole.y + (hole.hole_offset_y ?? 0)
296
- drawPill({
297
- ctx,
298
- center: { x: holeX, y: holeY },
299
- width: hole.hole_width,
300
- height: hole.hole_height,
301
- fill: colorMap.drill,
302
- realToCanvasMat,
303
- })
352
+ // If fully covered, draw soldermask overlay
353
+ if (isCoveredWithSoldermask) {
354
+ drawRect({
355
+ ctx,
356
+ center: { x: hole.x, y: hole.y },
357
+ width: hole.rect_pad_width,
358
+ height: hole.rect_pad_height,
359
+ fill: soldermaskRingColor,
360
+ realToCanvasMat,
361
+ borderRadius: hole.rect_border_radius ?? 0,
362
+ })
363
+ }
364
+
365
+ // Draw pill drill hole (with offset, only if not fully covered with soldermask)
366
+ if (!isCoveredWithSoldermask) {
367
+ const holeX = hole.x + (hole.hole_offset_x ?? 0)
368
+ const holeY = hole.y + (hole.hole_offset_y ?? 0)
369
+ drawPill({
370
+ ctx,
371
+ center: { x: holeX, y: holeY },
372
+ width: hole.hole_width,
373
+ height: hole.hole_height,
374
+ fill: colorMap.drill,
375
+ realToCanvasMat,
376
+ })
377
+ }
304
378
  return
305
379
  }
306
380
 
@@ -347,18 +421,34 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
347
421
  )
348
422
  }
349
423
 
350
- // Draw rotated pill drill hole (with offset)
351
- const holeX = hole.x + (hole.hole_offset_x ?? 0)
352
- const holeY = hole.y + (hole.hole_offset_y ?? 0)
353
- drawPill({
354
- ctx,
355
- center: { x: holeX, y: holeY },
356
- width: hole.hole_width,
357
- height: hole.hole_height,
358
- fill: colorMap.drill,
359
- realToCanvasMat,
360
- rotation: hole.hole_ccw_rotation,
361
- })
424
+ // If fully covered, draw soldermask overlay
425
+ if (isCoveredWithSoldermask) {
426
+ drawRect({
427
+ ctx,
428
+ center: { x: hole.x, y: hole.y },
429
+ width: hole.rect_pad_width,
430
+ height: hole.rect_pad_height,
431
+ fill: soldermaskRingColor,
432
+ realToCanvasMat,
433
+ borderRadius: hole.rect_border_radius ?? 0,
434
+ rotation: hole.rect_ccw_rotation,
435
+ })
436
+ }
437
+
438
+ // Draw rotated pill drill hole (with offset, only if not fully covered with soldermask)
439
+ if (!isCoveredWithSoldermask) {
440
+ const holeX = hole.x + (hole.hole_offset_x ?? 0)
441
+ const holeY = hole.y + (hole.hole_offset_y ?? 0)
442
+ drawPill({
443
+ ctx,
444
+ center: { x: holeX, y: holeY },
445
+ width: hole.hole_width,
446
+ height: hole.hole_height,
447
+ fill: colorMap.drill,
448
+ realToCanvasMat,
449
+ rotation: hole.hole_ccw_rotation,
450
+ })
451
+ }
362
452
  return
363
453
  }
364
454
 
@@ -380,46 +470,48 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
380
470
  })
381
471
  }
382
472
 
383
- // Draw drill hole (with offset)
384
- const holeX = hole.x + (hole.hole_offset_x ?? 0)
385
- const holeY = hole.y + (hole.hole_offset_y ?? 0)
386
- const holeShape = hole.hole_shape
473
+ // Draw drill hole (with offset, only if not fully covered with soldermask)
474
+ if (!isCoveredWithSoldermask) {
475
+ const holeX = hole.x + (hole.hole_offset_x ?? 0)
476
+ const holeY = hole.y + (hole.hole_offset_y ?? 0)
477
+ const holeShape = hole.hole_shape
387
478
 
388
- if (holeShape === "circle") {
389
- drawCircle({
390
- ctx,
391
- center: { x: holeX, y: holeY },
392
- radius: (hole.hole_diameter ?? 0) / 2,
393
- fill: colorMap.drill,
394
- realToCanvasMat,
395
- })
396
- } else if (holeShape === "oval") {
397
- drawOval({
398
- ctx,
399
- center: { x: holeX, y: holeY },
400
- radius_x: (hole.hole_width ?? 0) / 2,
401
- radius_y: (hole.hole_height ?? 0) / 2,
402
- fill: colorMap.drill,
403
- realToCanvasMat,
404
- })
405
- } else if (holeShape === "pill") {
406
- drawPill({
407
- ctx,
408
- center: { x: holeX, y: holeY },
409
- width: hole.hole_width ?? 0,
410
- height: hole.hole_height ?? 0,
411
- fill: colorMap.drill,
412
- realToCanvasMat,
413
- })
414
- } else if (holeShape === "rotated_pill") {
415
- drawPill({
416
- ctx,
417
- center: { x: holeX, y: holeY },
418
- width: hole.hole_width ?? 0,
419
- height: hole.hole_height ?? 0,
420
- fill: colorMap.drill,
421
- realToCanvasMat,
422
- })
479
+ if (holeShape === "circle") {
480
+ drawCircle({
481
+ ctx,
482
+ center: { x: holeX, y: holeY },
483
+ radius: (hole.hole_diameter ?? 0) / 2,
484
+ fill: colorMap.drill,
485
+ realToCanvasMat,
486
+ })
487
+ } else if (holeShape === "oval") {
488
+ drawOval({
489
+ ctx,
490
+ center: { x: holeX, y: holeY },
491
+ radius_x: (hole.hole_width ?? 0) / 2,
492
+ radius_y: (hole.hole_height ?? 0) / 2,
493
+ fill: colorMap.drill,
494
+ realToCanvasMat,
495
+ })
496
+ } else if (holeShape === "pill") {
497
+ drawPill({
498
+ ctx,
499
+ center: { x: holeX, y: holeY },
500
+ width: hole.hole_width ?? 0,
501
+ height: hole.hole_height ?? 0,
502
+ fill: colorMap.drill,
503
+ realToCanvasMat,
504
+ })
505
+ } else if (holeShape === "rotated_pill") {
506
+ drawPill({
507
+ ctx,
508
+ center: { x: holeX, y: holeY },
509
+ width: hole.hole_width ?? 0,
510
+ height: hole.hole_height ?? 0,
511
+ fill: colorMap.drill,
512
+ realToCanvasMat,
513
+ })
514
+ }
423
515
  }
424
516
  return
425
517
  }
@@ -33,7 +33,7 @@ function getSoldermaskColor(layer: string, colorMap: PcbColorMap): string {
33
33
  )
34
34
  }
35
35
 
36
- function getBorderRadius(pad: PcbSmtPad, margin: number = 0): number {
36
+ function getBorderRadius(pad: PcbSmtPad, margin = 0): number {
37
37
  return (
38
38
  ((pad as { corner_radius?: number }).corner_radius ??
39
39
  (pad as { rect_border_radius?: number }).rect_border_radius ??
@@ -46,13 +46,10 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
46
46
 
47
47
  const color = layerToColor(pad.layer, colorMap)
48
48
  const isCoveredWithSoldermask = pad.is_covered_with_solder_mask === true
49
- // When covered with soldermask, treat margin 0 or undefined as 0 positive margin
50
- const margin =
51
- isCoveredWithSoldermask && pad.soldermask_margin !== undefined
52
- ? pad.soldermask_margin
53
- : 0
49
+ // If covered with soldermask, fully covered with no margin; otherwise use soldermask_margin if set
50
+ const margin = isCoveredWithSoldermask ? 0 : (pad.soldermask_margin ?? 0)
54
51
  const hasSoldermask =
55
- isCoveredWithSoldermask &&
52
+ !isCoveredWithSoldermask &&
56
53
  pad.soldermask_margin !== undefined &&
57
54
  pad.soldermask_margin !== 0
58
55
  const soldermaskRingColor = getSoldermaskColor(pad.layer, colorMap)
@@ -61,8 +58,8 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
61
58
 
62
59
  // Draw the copper pad
63
60
  if (pad.shape === "rect") {
64
- // For positive margins (including 0), draw extended mask area first
65
- if (isCoveredWithSoldermask && margin >= 0) {
61
+ // For positive margins, draw extended mask area first
62
+ if (hasSoldermask && margin > 0) {
66
63
  drawRect({
67
64
  ctx,
68
65
  center: { x: pad.x, y: pad.y },
@@ -117,8 +114,8 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
117
114
  }
118
115
 
119
116
  if (pad.shape === "rotated_rect") {
120
- // For positive margins (including 0), draw extended mask area first
121
- if (isCoveredWithSoldermask && margin >= 0) {
117
+ // For positive margins, draw extended mask area first
118
+ if (hasSoldermask && margin > 0) {
122
119
  drawRect({
123
120
  ctx,
124
121
  center: { x: pad.x, y: pad.y },
@@ -176,8 +173,8 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
176
173
  }
177
174
 
178
175
  if (pad.shape === "circle") {
179
- // For positive margins (including 0), draw extended mask area first
180
- if (isCoveredWithSoldermask && margin >= 0) {
176
+ // For positive margins, draw extended mask area first
177
+ if (hasSoldermask && margin > 0) {
181
178
  drawCircle({
182
179
  ctx,
183
180
  center: { x: pad.x, y: pad.y },
@@ -223,8 +220,8 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
223
220
  }
224
221
 
225
222
  if (pad.shape === "pill") {
226
- // For positive margins (including 0), draw extended mask area first
227
- if (isCoveredWithSoldermask && margin >= 0) {
223
+ // For positive margins, draw extended mask area first
224
+ if (hasSoldermask && margin > 0) {
228
225
  drawPill({
229
226
  ctx,
230
227
  center: { x: pad.x, y: pad.y },
@@ -275,8 +272,8 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
275
272
  }
276
273
 
277
274
  if (pad.shape === "rotated_pill") {
278
- // For positive margins (including 0), draw extended mask area first
279
- if (isCoveredWithSoldermask && margin >= 0) {
275
+ // For positive margins, draw extended mask area first
276
+ if (hasSoldermask && margin > 0) {
280
277
  drawPill({
281
278
  ctx,
282
279
  center: { x: pad.x, y: pad.y },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "circuit-to-canvas",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.45",
4
+ "version": "0.0.46",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "tsup-node ./lib/index.ts --format esm --dts",