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.
Files changed (53) hide show
  1. package/dist/index.d.ts +10 -4
  2. package/dist/index.js +2911 -2719
  3. package/lib/drawer/CircuitToCanvasDrawer.ts +317 -366
  4. package/lib/drawer/elements/helper-functions/draw-pill.ts +39 -0
  5. package/lib/drawer/elements/helper-functions/draw-polygon.ts +25 -0
  6. package/lib/drawer/elements/helper-functions/draw-rounded-rect.ts +34 -0
  7. package/lib/drawer/elements/helper-functions/index.ts +3 -0
  8. package/lib/drawer/elements/pcb-board.ts +28 -13
  9. package/lib/drawer/elements/pcb-hole.ts +56 -338
  10. package/lib/drawer/elements/pcb-plated-hole.ts +154 -442
  11. package/lib/drawer/elements/pcb-smtpad.ts +3 -292
  12. package/lib/drawer/elements/pcb-soldermask/board.ts +44 -0
  13. package/lib/drawer/elements/pcb-soldermask/cutout.ts +74 -0
  14. package/lib/drawer/elements/pcb-soldermask/hole.ts +288 -0
  15. package/lib/drawer/elements/pcb-soldermask/index.ts +140 -0
  16. package/lib/drawer/elements/pcb-soldermask/plated-hole.ts +365 -0
  17. package/lib/drawer/elements/pcb-soldermask/smt-pad.ts +354 -0
  18. package/lib/drawer/elements/pcb-soldermask/via.ts +27 -0
  19. package/package.json +1 -1
  20. package/tests/board-snapshot/__snapshots__/usb-c-flashlight-board.snap.png +0 -0
  21. package/tests/board-snapshot/usb-c-flashlight-board.test.ts +1 -0
  22. package/tests/elements/__snapshots__/board-with-elements.snap.png +0 -0
  23. package/tests/elements/__snapshots__/brep-copper-pours.snap.png +0 -0
  24. package/tests/elements/__snapshots__/custom-outline-board.snap.png +0 -0
  25. package/tests/elements/__snapshots__/oval-plated-hole.snap.png +0 -0
  26. package/tests/elements/__snapshots__/pcb-board.snap.png +0 -0
  27. package/tests/elements/__snapshots__/pcb-comprehensive-soldermask-margin.snap.png +0 -0
  28. package/tests/elements/__snapshots__/pcb-fabrication-note-dimension.snap.png +0 -0
  29. package/tests/elements/__snapshots__/pcb-hole-soldermask-margin.snap.png +0 -0
  30. package/tests/elements/__snapshots__/pcb-keepout-layer-filter.snap.png +0 -0
  31. package/tests/elements/__snapshots__/pcb-keepout-multiple-layers.snap.png +0 -0
  32. package/tests/elements/__snapshots__/pcb-keepout-rect-and-circle.snap.png +0 -0
  33. package/tests/elements/__snapshots__/pcb-keepout-with-group-id.snap.png +0 -0
  34. package/tests/elements/__snapshots__/pcb-no-soldermask.snap.png +0 -0
  35. package/tests/elements/__snapshots__/pcb-plated-hole-soldermask-margin.snap.png +0 -0
  36. package/tests/elements/__snapshots__/pcb-plated-hole.snap.png +0 -0
  37. package/tests/elements/__snapshots__/pcb-silkscreen-on-component.snap.png +0 -0
  38. package/tests/elements/__snapshots__/pcb-silkscreen-oval.snap.png +0 -0
  39. package/tests/elements/__snapshots__/pcb-smtpad-asymmetric-soldermask-margin.snap.png +0 -0
  40. package/tests/elements/__snapshots__/pcb-smtpad-soldermask-coverage.snap.png +0 -0
  41. package/tests/elements/__snapshots__/pcb-smtpad-soldermask-margin.snap.png +0 -0
  42. package/tests/elements/__snapshots__/pill-plated-hole.snap.png +0 -0
  43. package/tests/elements/pcb-board.test.ts +2 -2
  44. package/tests/elements/pcb-comprehensive-soldermask-margin.test.ts +2 -2
  45. package/tests/elements/pcb-hole-soldermask-margin.test.ts +155 -2
  46. package/tests/elements/pcb-keepout-with-group-id.test.ts +1 -1
  47. package/tests/elements/pcb-no-soldermask.test.ts +1281 -0
  48. package/tests/elements/pcb-plated-hole-soldermask-margin.test.ts +1 -1
  49. package/tests/elements/pcb-plated-hole.test.ts +40 -4
  50. package/tests/elements/pcb-smtpad-asymmetric-soldermask-margin.test.ts +1 -1
  51. package/tests/elements/pcb-smtpad-soldermask-coverage.test.ts +1 -1
  52. package/tests/elements/pcb-smtpad-soldermask-margin.test.ts +1 -1
  53. package/tests/fixtures/getStackedPngSvgComparison.ts +10 -4
@@ -0,0 +1,39 @@
1
+ import type { CanvasContext } from "../../types"
2
+
3
+ /**
4
+ * Draws a pill/stadium shape path centered at (cx, cy).
5
+ * A pill is a rectangle with fully rounded ends (semicircular caps).
6
+ * The path is not filled or stroked - call ctx.fill() or ctx.stroke() after.
7
+ */
8
+ export function drawPillPath(params: {
9
+ ctx: CanvasContext
10
+ cx: number
11
+ cy: number
12
+ width: number
13
+ height: number
14
+ }): void {
15
+ const { ctx, cx, cy, width, height } = params
16
+ if (width > height) {
17
+ // Horizontal pill
18
+ const radius = height / 2
19
+ const straightLength = width - height
20
+ ctx.moveTo(cx - straightLength / 2, cy - radius)
21
+ ctx.lineTo(cx + straightLength / 2, cy - radius)
22
+ ctx.arc(cx + straightLength / 2, cy, radius, -Math.PI / 2, Math.PI / 2)
23
+ ctx.lineTo(cx - straightLength / 2, cy + radius)
24
+ ctx.arc(cx - straightLength / 2, cy, radius, Math.PI / 2, -Math.PI / 2)
25
+ } else if (height > width) {
26
+ // Vertical pill
27
+ const radius = width / 2
28
+ const straightLength = height - width
29
+ ctx.moveTo(cx + radius, cy - straightLength / 2)
30
+ ctx.lineTo(cx + radius, cy + straightLength / 2)
31
+ ctx.arc(cx, cy + straightLength / 2, radius, 0, Math.PI)
32
+ ctx.lineTo(cx - radius, cy - straightLength / 2)
33
+ ctx.arc(cx, cy - straightLength / 2, radius, Math.PI, 0)
34
+ } else {
35
+ // Square dimensions = circle
36
+ ctx.arc(cx, cy, width / 2, 0, Math.PI * 2)
37
+ }
38
+ ctx.closePath()
39
+ }
@@ -0,0 +1,25 @@
1
+ import type { CanvasContext } from "../../types"
2
+
3
+ /**
4
+ * Draws a polygon path from an array of points.
5
+ * The path is not filled or stroked - call ctx.fill() or ctx.stroke() after.
6
+ */
7
+ export function drawPolygonPath(params: {
8
+ ctx: CanvasContext
9
+ points: Array<{ x: number; y: number }>
10
+ }): void {
11
+ const { ctx, points } = params
12
+ if (points.length < 3) return
13
+
14
+ const firstPoint = points[0]
15
+ if (firstPoint) {
16
+ ctx.moveTo(firstPoint.x, firstPoint.y)
17
+ for (let i = 1; i < points.length; i++) {
18
+ const point = points[i]
19
+ if (point) {
20
+ ctx.lineTo(point.x, point.y)
21
+ }
22
+ }
23
+ ctx.closePath()
24
+ }
25
+ }
@@ -0,0 +1,34 @@
1
+ import type { CanvasContext } from "../../types"
2
+
3
+ /**
4
+ * Draws a rounded rectangle path centered at (cx, cy).
5
+ * The path is not filled or stroked - call ctx.fill() or ctx.stroke() after.
6
+ */
7
+ export function drawRoundedRectPath(params: {
8
+ ctx: CanvasContext
9
+ cx: number
10
+ cy: number
11
+ width: number
12
+ height: number
13
+ radius: number
14
+ }): void {
15
+ const { ctx, cx, cy, width, height, radius } = params
16
+ const x = cx - width / 2
17
+ const y = cy - height / 2
18
+ const r = Math.min(radius, width / 2, height / 2)
19
+
20
+ if (r > 0) {
21
+ ctx.moveTo(x + r, y)
22
+ ctx.lineTo(x + width - r, y)
23
+ ctx.arcTo(x + width, y, x + width, y + r, r)
24
+ ctx.lineTo(x + width, y + height - r)
25
+ ctx.arcTo(x + width, y + height, x + width - r, y + height, r)
26
+ ctx.lineTo(x + r, y + height)
27
+ ctx.arcTo(x, y + height, x, y + height - r, r)
28
+ ctx.lineTo(x, y + r)
29
+ ctx.arcTo(x, y, x + r, y, r)
30
+ } else {
31
+ ctx.rect(x, y, width, height)
32
+ }
33
+ ctx.closePath()
34
+ }
@@ -0,0 +1,3 @@
1
+ export { drawPillPath } from "./draw-pill"
2
+ export { drawPolygonPath } from "./draw-polygon"
3
+ export { drawRoundedRectPath } from "./draw-rounded-rect"
@@ -1,22 +1,35 @@
1
1
  import type { PcbBoard } from "circuit-json"
2
2
  import type { Matrix } from "transformation-matrix"
3
- import type { PcbColorMap, CanvasContext } from "../types"
4
3
  import { drawPath } from "../shapes/path"
5
4
  import { drawRect } from "../shapes/rect"
5
+ import type { CanvasContext, PcbColorMap } from "../types"
6
6
 
7
7
  export interface DrawPcbBoardParams {
8
8
  ctx: CanvasContext
9
9
  board: PcbBoard
10
10
  realToCanvasMat: Matrix
11
11
  colorMap: PcbColorMap
12
+ drawBoardMaterial: boolean
12
13
  }
13
14
 
14
15
  export function drawPcbBoard(params: DrawPcbBoardParams): void {
15
- const { ctx, board, realToCanvasMat, colorMap } = params
16
+ const { ctx, board, realToCanvasMat, colorMap, drawBoardMaterial } = params
16
17
  const { width, height, center, outline } = board
17
18
 
18
- // If the board has a custom outline, draw it as a path
19
+ // If the board has a custom outline, draw substrate and outline
19
20
  if (outline && Array.isArray(outline) && outline.length >= 3) {
21
+ // Draw substrate fill only if drawBoardMaterial is true
22
+ if (drawBoardMaterial) {
23
+ drawPath({
24
+ ctx,
25
+ points: outline.map((p) => ({ x: p.x, y: p.y })),
26
+ fill: colorMap.substrate,
27
+ realToCanvasMat,
28
+ closePath: true,
29
+ })
30
+ }
31
+
32
+ // Always draw outline stroke
20
33
  drawPath({
21
34
  ctx,
22
35
  points: outline.map((p) => ({ x: p.x, y: p.y })),
@@ -30,17 +43,19 @@ export function drawPcbBoard(params: DrawPcbBoardParams): void {
30
43
 
31
44
  // Otherwise draw a rectangle
32
45
  if (width !== undefined && height !== undefined && center) {
33
- // Draw the board outline as a rectangle stroke
34
- drawRect({
35
- ctx,
36
- center,
37
- width,
38
- height,
39
- fill: "transparent",
40
- realToCanvasMat,
41
- })
46
+ // Draw substrate fill only if drawBoardMaterial is true
47
+ if (drawBoardMaterial) {
48
+ drawRect({
49
+ ctx,
50
+ center,
51
+ width,
52
+ height,
53
+ fill: colorMap.substrate,
54
+ realToCanvasMat,
55
+ })
56
+ }
42
57
 
43
- // Draw the outline stroke separately using path
58
+ // Always draw the outline stroke separately using path
44
59
  const halfWidth = width / 2
45
60
  const halfHeight = height / 2
46
61
  const corners = [
@@ -1,26 +1,21 @@
1
- import type { PCBHole } from "circuit-json"
1
+ import type { PcbHole } from "circuit-json"
2
2
  import type { Matrix } from "transformation-matrix"
3
3
  import type { PcbColorMap, CanvasContext } from "../types"
4
4
  import { drawCircle } from "../shapes/circle"
5
5
  import { drawRect } from "../shapes/rect"
6
6
  import { drawOval } from "../shapes/oval"
7
7
  import { drawPill } from "../shapes/pill"
8
- import {
9
- drawSoldermaskRingForCircle,
10
- drawSoldermaskRingForOval,
11
- drawSoldermaskRingForPill,
12
- drawSoldermaskRingForRect,
13
- } from "./soldermask-margin"
14
8
 
15
9
  export interface DrawPcbHoleParams {
16
10
  ctx: CanvasContext
17
- hole: PCBHole
11
+ hole: PcbHole
18
12
  realToCanvasMat: Matrix
19
13
  colorMap: PcbColorMap
14
+ soldermaskMargin?: number
20
15
  }
21
16
 
22
17
  // Helper function to safely access ccw_rotation property
23
- function getRotation(hole: PCBHole): number {
18
+ function getRotation(hole: PcbHole): number {
24
19
  if ("ccw_rotation" in hole && typeof hole.ccw_rotation === "number") {
25
20
  return hole.ccw_rotation
26
21
  }
@@ -28,357 +23,80 @@ function getRotation(hole: PCBHole): number {
28
23
  }
29
24
 
30
25
  export function drawPcbHole(params: DrawPcbHoleParams): void {
31
- const { ctx, hole, realToCanvasMat, colorMap } = params
26
+ const { ctx, hole, realToCanvasMat, colorMap, soldermaskMargin = 0 } = params
32
27
 
33
- const isCoveredWithSoldermask = hole.is_covered_with_solder_mask === true
34
- const margin = isCoveredWithSoldermask ? 0 : (hole.soldermask_margin ?? 0)
35
- const hasSoldermask =
36
- !isCoveredWithSoldermask &&
37
- hole.soldermask_margin !== undefined &&
38
- hole.soldermask_margin !== 0
39
- const positiveMarginColor = colorMap.substrate
40
- const soldermaskOverlayColor = colorMap.soldermaskOverCopper.top
41
- const soldermaskRingColor = colorMap.soldermaskOverCopper.top
42
-
43
- if (hole.hole_shape === "circle") {
44
- // For positive margins, draw extended mask area first
45
- if (hasSoldermask && margin > 0) {
46
- drawCircle({
47
- ctx,
48
- center: { x: hole.x, y: hole.y },
49
- radius: hole.hole_diameter / 2 + margin,
50
- fill: positiveMarginColor,
51
- realToCanvasMat,
52
- })
53
- }
54
-
55
- // Draw the hole (only if not fully covered with soldermask)
56
- if (!isCoveredWithSoldermask) {
57
- drawCircle({
58
- ctx,
59
- center: { x: hole.x, y: hole.y },
60
- radius: hole.hole_diameter / 2,
61
- fill: colorMap.drill,
62
- realToCanvasMat,
63
- })
28
+ // Skip drawing if the hole is fully covered with soldermask
29
+ if (hole.is_covered_with_solder_mask === true) {
30
+ return
31
+ }
64
32
 
65
- // For negative margins, draw soldermask ring on top of the hole
66
- if (hasSoldermask && margin < 0) {
67
- drawSoldermaskRingForCircle(
68
- ctx,
69
- { x: hole.x, y: hole.y },
70
- hole.hole_diameter / 2,
71
- margin,
72
- realToCanvasMat,
73
- soldermaskRingColor,
74
- colorMap.drill,
75
- )
76
- }
77
- }
33
+ // For negative margins, draw smaller hole (inset by margin amount)
34
+ const holeInset = soldermaskMargin < 0 ? Math.abs(soldermaskMargin) : 0
78
35
 
79
- // If fully covered, draw soldermask overlay
80
- if (isCoveredWithSoldermask) {
81
- drawCircle({
82
- ctx,
83
- center: { x: hole.x, y: hole.y },
84
- radius: hole.hole_diameter / 2,
85
- fill: soldermaskOverlayColor,
86
- realToCanvasMat,
87
- })
88
- }
36
+ if (hole.hole_shape === "circle") {
37
+ drawCircle({
38
+ ctx,
39
+ center: { x: hole.x, y: hole.y },
40
+ radius: hole.hole_diameter / 2 - holeInset,
41
+ fill: colorMap.drill,
42
+ realToCanvasMat,
43
+ })
89
44
  return
90
45
  }
91
46
 
92
47
  if (hole.hole_shape === "square") {
93
48
  const rotation = getRotation(hole)
94
- // For positive margins, draw extended mask area first
95
- if (hasSoldermask && margin > 0) {
96
- drawRect({
97
- ctx,
98
- center: { x: hole.x, y: hole.y },
99
- width: hole.hole_diameter + margin * 2,
100
- height: hole.hole_diameter + margin * 2,
101
- fill: positiveMarginColor,
102
- realToCanvasMat,
103
- rotation,
104
- })
105
- }
106
-
107
- // Draw the hole (only if not fully covered with soldermask)
108
- if (!isCoveredWithSoldermask) {
109
- drawRect({
110
- ctx,
111
- center: { x: hole.x, y: hole.y },
112
- width: hole.hole_diameter,
113
- height: hole.hole_diameter,
114
- fill: colorMap.drill,
115
- realToCanvasMat,
116
- rotation,
117
- })
118
-
119
- // For negative margins, draw soldermask ring on top of the hole
120
- if (hasSoldermask && margin < 0) {
121
- drawSoldermaskRingForRect(
122
- ctx,
123
- { x: hole.x, y: hole.y },
124
- hole.hole_diameter,
125
- hole.hole_diameter,
126
- margin,
127
- 0,
128
- rotation,
129
- realToCanvasMat,
130
- soldermaskRingColor,
131
- colorMap.drill,
132
- )
133
- }
134
- }
135
-
136
- // If fully covered, draw soldermask overlay
137
- if (isCoveredWithSoldermask) {
138
- drawRect({
139
- ctx,
140
- center: { x: hole.x, y: hole.y },
141
- width: hole.hole_diameter,
142
- height: hole.hole_diameter,
143
- fill: soldermaskOverlayColor,
144
- realToCanvasMat,
145
- rotation,
146
- })
147
- }
49
+ drawRect({
50
+ ctx,
51
+ center: { x: hole.x, y: hole.y },
52
+ width: hole.hole_diameter - holeInset * 2,
53
+ height: hole.hole_diameter - holeInset * 2,
54
+ fill: colorMap.drill,
55
+ realToCanvasMat,
56
+ rotation,
57
+ })
148
58
  return
149
59
  }
150
60
 
151
61
  if (hole.hole_shape === "oval") {
152
62
  const rotation = getRotation(hole)
153
- // For positive margins, draw extended mask area first
154
- if (hasSoldermask && margin > 0) {
155
- drawOval({
156
- ctx,
157
- center: { x: hole.x, y: hole.y },
158
- radius_x: hole.hole_width / 2 + margin,
159
- radius_y: hole.hole_height / 2 + margin,
160
- fill: positiveMarginColor,
161
- realToCanvasMat,
162
- rotation,
163
- })
164
- }
165
-
166
- // Draw the hole (only if not fully covered with soldermask)
167
- if (!isCoveredWithSoldermask) {
168
- drawOval({
169
- ctx,
170
- center: { x: hole.x, y: hole.y },
171
- radius_x: hole.hole_width / 2,
172
- radius_y: hole.hole_height / 2,
173
- fill: colorMap.drill,
174
- realToCanvasMat,
175
- rotation,
176
- })
177
-
178
- // For negative margins, draw soldermask ring on top of the hole
179
- if (hasSoldermask && margin < 0) {
180
- drawSoldermaskRingForOval(
181
- ctx,
182
- { x: hole.x, y: hole.y },
183
- hole.hole_width / 2,
184
- hole.hole_height / 2,
185
- margin,
186
- rotation,
187
- realToCanvasMat,
188
- soldermaskRingColor,
189
- colorMap.drill,
190
- )
191
- }
192
- }
193
-
194
- // If fully covered, draw soldermask overlay
195
- if (isCoveredWithSoldermask) {
196
- drawOval({
197
- ctx,
198
- center: { x: hole.x, y: hole.y },
199
- radius_x: hole.hole_width / 2,
200
- radius_y: hole.hole_height / 2,
201
- fill: soldermaskOverlayColor,
202
- realToCanvasMat,
203
- rotation,
204
- })
205
- }
63
+ drawOval({
64
+ ctx,
65
+ center: { x: hole.x, y: hole.y },
66
+ radius_x: hole.hole_width / 2 - holeInset,
67
+ radius_y: hole.hole_height / 2 - holeInset,
68
+ fill: colorMap.drill,
69
+ realToCanvasMat,
70
+ rotation,
71
+ })
206
72
  return
207
73
  }
208
74
 
209
75
  if (hole.hole_shape === "rect") {
210
76
  const rotation = getRotation(hole)
211
- // For positive margins, draw extended mask area first
212
- if (hasSoldermask && margin > 0) {
213
- drawRect({
214
- ctx,
215
- center: { x: hole.x, y: hole.y },
216
- width: hole.hole_width + margin * 2,
217
- height: hole.hole_height + margin * 2,
218
- fill: positiveMarginColor,
219
- realToCanvasMat,
220
- rotation,
221
- })
222
- }
223
-
224
- // Draw the hole (only if not fully covered with soldermask)
225
- if (!isCoveredWithSoldermask) {
226
- drawRect({
227
- ctx,
228
- center: { x: hole.x, y: hole.y },
229
- width: hole.hole_width,
230
- height: hole.hole_height,
231
- fill: colorMap.drill,
232
- realToCanvasMat,
233
- rotation,
234
- })
235
-
236
- // For negative margins, draw soldermask ring on top of the hole
237
- if (hasSoldermask && margin < 0) {
238
- drawSoldermaskRingForRect(
239
- ctx,
240
- { x: hole.x, y: hole.y },
241
- hole.hole_width,
242
- hole.hole_height,
243
- margin,
244
- 0,
245
- rotation,
246
- realToCanvasMat,
247
- soldermaskRingColor,
248
- colorMap.drill,
249
- )
250
- }
251
- }
252
-
253
- // If fully covered, draw soldermask overlay
254
- if (isCoveredWithSoldermask) {
255
- drawRect({
256
- ctx,
257
- center: { x: hole.x, y: hole.y },
258
- width: hole.hole_width,
259
- height: hole.hole_height,
260
- fill: soldermaskOverlayColor,
261
- realToCanvasMat,
262
- rotation,
263
- })
264
- }
265
- return
266
- }
267
-
268
- if (hole.hole_shape === "pill") {
269
- const rotation = getRotation(hole)
270
- // For positive margins, draw extended mask area first
271
- if (hasSoldermask && margin > 0) {
272
- drawPill({
273
- ctx,
274
- center: { x: hole.x, y: hole.y },
275
- width: hole.hole_width + margin * 2,
276
- height: hole.hole_height + margin * 2,
277
- fill: positiveMarginColor,
278
- realToCanvasMat,
279
- rotation,
280
- })
281
- }
282
-
283
- // Draw the hole (only if not fully covered with soldermask)
284
- if (!isCoveredWithSoldermask) {
285
- drawPill({
286
- ctx,
287
- center: { x: hole.x, y: hole.y },
288
- width: hole.hole_width,
289
- height: hole.hole_height,
290
- fill: colorMap.drill,
291
- realToCanvasMat,
292
- rotation,
293
- })
294
-
295
- // For negative margins, draw soldermask ring on top of the hole
296
- if (hasSoldermask && margin < 0) {
297
- drawSoldermaskRingForPill(
298
- ctx,
299
- { x: hole.x, y: hole.y },
300
- hole.hole_width,
301
- hole.hole_height,
302
- margin,
303
- rotation,
304
- realToCanvasMat,
305
- soldermaskRingColor,
306
- colorMap.drill,
307
- )
308
- }
309
- }
310
-
311
- // If fully covered, draw soldermask overlay
312
- if (isCoveredWithSoldermask) {
313
- drawPill({
314
- ctx,
315
- center: { x: hole.x, y: hole.y },
316
- width: hole.hole_width,
317
- height: hole.hole_height,
318
- fill: soldermaskOverlayColor,
319
- realToCanvasMat,
320
- rotation,
321
- })
322
- }
77
+ drawRect({
78
+ ctx,
79
+ center: { x: hole.x, y: hole.y },
80
+ width: hole.hole_width - holeInset * 2,
81
+ height: hole.hole_height - holeInset * 2,
82
+ fill: colorMap.drill,
83
+ realToCanvasMat,
84
+ rotation,
85
+ })
323
86
  return
324
87
  }
325
88
 
326
- if (hole.hole_shape === "rotated_pill") {
89
+ if (hole.hole_shape === "pill" || hole.hole_shape === "rotated_pill") {
327
90
  const rotation = getRotation(hole)
328
-
329
- // For positive margins, draw extended mask area first
330
- if (hasSoldermask && margin > 0) {
331
- drawPill({
332
- ctx,
333
- center: { x: hole.x, y: hole.y },
334
- width: hole.hole_width + margin * 2,
335
- height: hole.hole_height + margin * 2,
336
- fill: positiveMarginColor,
337
- realToCanvasMat,
338
- rotation,
339
- })
340
- }
341
-
342
- // Draw the hole (only if not fully covered with soldermask)
343
- if (!isCoveredWithSoldermask) {
344
- drawPill({
345
- ctx,
346
- center: { x: hole.x, y: hole.y },
347
- width: hole.hole_width,
348
- height: hole.hole_height,
349
- fill: colorMap.drill,
350
- realToCanvasMat,
351
- rotation,
352
- })
353
-
354
- // For negative margins, draw soldermask ring on top of the hole
355
- if (hasSoldermask && margin < 0) {
356
- drawSoldermaskRingForPill(
357
- ctx,
358
- { x: hole.x, y: hole.y },
359
- hole.hole_width,
360
- hole.hole_height,
361
- margin,
362
- rotation,
363
- realToCanvasMat,
364
- soldermaskRingColor,
365
- colorMap.drill,
366
- )
367
- }
368
- }
369
-
370
- // If fully covered, draw soldermask overlay
371
- if (isCoveredWithSoldermask) {
372
- drawPill({
373
- ctx,
374
- center: { x: hole.x, y: hole.y },
375
- width: hole.hole_width,
376
- height: hole.hole_height,
377
- fill: soldermaskOverlayColor,
378
- realToCanvasMat,
379
- rotation,
380
- })
381
- }
91
+ drawPill({
92
+ ctx,
93
+ center: { x: hole.x, y: hole.y },
94
+ width: hole.hole_width - holeInset * 2,
95
+ height: hole.hole_height - holeInset * 2,
96
+ fill: colorMap.drill,
97
+ realToCanvasMat,
98
+ rotation,
99
+ })
382
100
  return
383
101
  }
384
102
  }