circuit-to-canvas 0.0.30 → 0.0.32
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 +41 -5
- package/dist/index.js +456 -41
- package/lib/drawer/CircuitToCanvasDrawer.ts +116 -2
- package/lib/drawer/elements/index.ts +10 -0
- package/lib/drawer/elements/pcb-hole.ts +2 -2
- package/lib/drawer/elements/pcb-plated-hole.ts +6 -6
- package/lib/drawer/elements/pcb-silkscreen-oval.ts +35 -0
- package/lib/drawer/elements/pcb-smtpad.ts +174 -0
- package/lib/drawer/elements/soldermask-margin.ts +266 -0
- package/lib/drawer/shapes/oval.ts +25 -10
- package/package.json +3 -3
- package/tests/elements/__snapshots__/pcb-silkscreen-oval.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-smtpad-soldermask-margin.snap.png +0 -0
- package/tests/elements/pcb-silkscreen-oval.test.ts +37 -0
- package/tests/elements/pcb-smtpad-soldermask-margin.test.ts +163 -0
- package/tests/shapes/__snapshots__/oval.snap.png +0 -0
- package/tests/shapes/oval.test.ts +3 -2
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
PcbSilkscreenRect,
|
|
11
11
|
PcbSilkscreenCircle,
|
|
12
12
|
PcbSilkscreenLine,
|
|
13
|
+
PcbSilkscreenOval,
|
|
13
14
|
PcbSilkscreenPath,
|
|
14
15
|
PcbSilkscreenPill,
|
|
15
16
|
PcbCutout,
|
|
@@ -25,8 +26,14 @@ import type {
|
|
|
25
26
|
PcbNoteLine,
|
|
26
27
|
PcbRenderLayer,
|
|
27
28
|
} from "circuit-json"
|
|
29
|
+
import {
|
|
30
|
+
identity,
|
|
31
|
+
compose,
|
|
32
|
+
translate,
|
|
33
|
+
scale,
|
|
34
|
+
applyToPoint,
|
|
35
|
+
} from "transformation-matrix"
|
|
28
36
|
import { shouldDrawElement } from "./pcb-render-layer-filter"
|
|
29
|
-
import { identity, compose, translate, scale } from "transformation-matrix"
|
|
30
37
|
import type { Matrix } from "transformation-matrix"
|
|
31
38
|
import {
|
|
32
39
|
type CanvasContext,
|
|
@@ -41,11 +48,14 @@ import { drawPcbHole } from "./elements/pcb-hole"
|
|
|
41
48
|
import { drawPcbSmtPad } from "./elements/pcb-smtpad"
|
|
42
49
|
import { drawPcbTrace } from "./elements/pcb-trace"
|
|
43
50
|
import { drawPcbBoard } from "./elements/pcb-board"
|
|
51
|
+
import { drawPath } from "./shapes/path"
|
|
52
|
+
import { drawRect } from "./shapes/rect"
|
|
44
53
|
import { drawPcbSilkscreenText } from "./elements/pcb-silkscreen-text"
|
|
45
54
|
import { drawPcbSilkscreenRect } from "./elements/pcb-silkscreen-rect"
|
|
46
55
|
import { drawPcbSilkscreenCircle } from "./elements/pcb-silkscreen-circle"
|
|
47
56
|
import { drawPcbSilkscreenLine } from "./elements/pcb-silkscreen-line"
|
|
48
57
|
import { drawPcbSilkscreenPath } from "./elements/pcb-silkscreen-path"
|
|
58
|
+
import { drawPcbSilkscreenOval } from "./elements/pcb-silkscreen-oval"
|
|
49
59
|
import { drawPcbSilkscreenPill } from "./elements/pcb-silkscreen-pill"
|
|
50
60
|
import { drawPcbCutout } from "./elements/pcb-cutout"
|
|
51
61
|
import { drawPcbCopperPour } from "./elements/pcb-copper-pour"
|
|
@@ -148,8 +158,103 @@ export class CircuitToCanvasDrawer {
|
|
|
148
158
|
elements: AnyCircuitElement[],
|
|
149
159
|
options: DrawElementsOptions = {},
|
|
150
160
|
): void {
|
|
161
|
+
// Check if any pad has is_covered_with_solder_mask: true
|
|
162
|
+
const hasSoldermaskPads = elements.some(
|
|
163
|
+
(el) =>
|
|
164
|
+
el.type === "pcb_smtpad" &&
|
|
165
|
+
(el as PcbSmtPad).is_covered_with_solder_mask === true,
|
|
166
|
+
)
|
|
167
|
+
|
|
151
168
|
for (const element of elements) {
|
|
152
|
-
|
|
169
|
+
if (element.type === "pcb_board" && hasSoldermaskPads) {
|
|
170
|
+
// Draw board with soldermask fill when pads have soldermask
|
|
171
|
+
this.drawBoardWithSoldermask(element as PcbBoard)
|
|
172
|
+
} else {
|
|
173
|
+
this.drawElement(element, options)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private drawBoardWithSoldermask(board: PcbBoard): void {
|
|
179
|
+
const { width, height, center, outline } = board
|
|
180
|
+
const layer = "top" // Default to top layer for soldermask color
|
|
181
|
+
|
|
182
|
+
// If the board has a custom outline, draw it as a path with soldermask fill
|
|
183
|
+
if (outline && Array.isArray(outline) && outline.length >= 3) {
|
|
184
|
+
const soldermaskColor =
|
|
185
|
+
this.colorMap.soldermask[
|
|
186
|
+
layer as keyof typeof this.colorMap.soldermask
|
|
187
|
+
] ?? this.colorMap.soldermask.top
|
|
188
|
+
|
|
189
|
+
// Draw filled path
|
|
190
|
+
const canvasPoints = outline.map((p) => {
|
|
191
|
+
const [x, y] = applyToPoint(this.realToCanvasMat, [p.x, p.y])
|
|
192
|
+
return { x, y }
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
this.ctx.beginPath()
|
|
196
|
+
const firstPoint = canvasPoints[0]
|
|
197
|
+
if (firstPoint) {
|
|
198
|
+
this.ctx.moveTo(firstPoint.x, firstPoint.y)
|
|
199
|
+
for (let i = 1; i < canvasPoints.length; i++) {
|
|
200
|
+
const point = canvasPoints[i]
|
|
201
|
+
if (point) {
|
|
202
|
+
this.ctx.lineTo(point.x, point.y)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
this.ctx.closePath()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
this.ctx.fillStyle = soldermaskColor
|
|
209
|
+
this.ctx.fill()
|
|
210
|
+
|
|
211
|
+
// Draw outline stroke
|
|
212
|
+
drawPath({
|
|
213
|
+
ctx: this.ctx,
|
|
214
|
+
points: outline.map((p) => ({ x: p.x, y: p.y })),
|
|
215
|
+
stroke: this.colorMap.boardOutline,
|
|
216
|
+
strokeWidth: 0.1,
|
|
217
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
218
|
+
closePath: true,
|
|
219
|
+
})
|
|
220
|
+
return
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Otherwise draw a rectangle with soldermask fill
|
|
224
|
+
if (width !== undefined && height !== undefined && center) {
|
|
225
|
+
const soldermaskColor =
|
|
226
|
+
this.colorMap.soldermask[
|
|
227
|
+
layer as keyof typeof this.colorMap.soldermask
|
|
228
|
+
] ?? this.colorMap.soldermask.top
|
|
229
|
+
|
|
230
|
+
// Draw filled rectangle
|
|
231
|
+
drawRect({
|
|
232
|
+
ctx: this.ctx,
|
|
233
|
+
center,
|
|
234
|
+
width,
|
|
235
|
+
height,
|
|
236
|
+
fill: soldermaskColor,
|
|
237
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
// Draw the outline stroke separately using path
|
|
241
|
+
const halfWidth = width / 2
|
|
242
|
+
const halfHeight = height / 2
|
|
243
|
+
const corners = [
|
|
244
|
+
{ x: center.x - halfWidth, y: center.y - halfHeight },
|
|
245
|
+
{ x: center.x + halfWidth, y: center.y - halfHeight },
|
|
246
|
+
{ x: center.x + halfWidth, y: center.y + halfHeight },
|
|
247
|
+
{ x: center.x - halfWidth, y: center.y + halfHeight },
|
|
248
|
+
]
|
|
249
|
+
|
|
250
|
+
drawPath({
|
|
251
|
+
ctx: this.ctx,
|
|
252
|
+
points: corners,
|
|
253
|
+
stroke: this.colorMap.boardOutline,
|
|
254
|
+
strokeWidth: 0.1,
|
|
255
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
256
|
+
closePath: true,
|
|
257
|
+
})
|
|
153
258
|
}
|
|
154
259
|
}
|
|
155
260
|
|
|
@@ -270,6 +375,15 @@ export class CircuitToCanvasDrawer {
|
|
|
270
375
|
})
|
|
271
376
|
}
|
|
272
377
|
|
|
378
|
+
if (element.type === "pcb_silkscreen_oval") {
|
|
379
|
+
drawPcbSilkscreenOval({
|
|
380
|
+
ctx: this.ctx,
|
|
381
|
+
oval: element as PcbSilkscreenOval,
|
|
382
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
383
|
+
colorMap: this.colorMap,
|
|
384
|
+
})
|
|
385
|
+
}
|
|
386
|
+
|
|
273
387
|
if (element.type === "pcb_cutout") {
|
|
274
388
|
drawPcbCutout({
|
|
275
389
|
ctx: this.ctx,
|
|
@@ -8,6 +8,11 @@ export { drawPcbVia, type DrawPcbViaParams } from "./pcb-via"
|
|
|
8
8
|
export { drawPcbHole, type DrawPcbHoleParams } from "./pcb-hole"
|
|
9
9
|
|
|
10
10
|
export { drawPcbSmtPad, type DrawPcbSmtPadParams } from "./pcb-smtpad"
|
|
11
|
+
export {
|
|
12
|
+
drawSoldermaskRingForRect,
|
|
13
|
+
drawSoldermaskRingForCircle,
|
|
14
|
+
drawSoldermaskRingForPill,
|
|
15
|
+
} from "./soldermask-margin"
|
|
11
16
|
|
|
12
17
|
export { drawPcbTrace, type DrawPcbTraceParams } from "./pcb-trace"
|
|
13
18
|
|
|
@@ -43,6 +48,11 @@ export {
|
|
|
43
48
|
type DrawPcbSilkscreenPillParams,
|
|
44
49
|
} from "./pcb-silkscreen-pill"
|
|
45
50
|
|
|
51
|
+
export {
|
|
52
|
+
drawPcbSilkscreenOval,
|
|
53
|
+
type DrawPcbSilkscreenOvalParams,
|
|
54
|
+
} from "./pcb-silkscreen-oval"
|
|
55
|
+
|
|
46
56
|
export { drawPcbCutout, type DrawPcbCutoutParams } from "./pcb-cutout"
|
|
47
57
|
|
|
48
58
|
export {
|
|
@@ -43,8 +43,8 @@ export function drawPcbHole(params: DrawPcbHoleParams): void {
|
|
|
43
43
|
drawOval({
|
|
44
44
|
ctx,
|
|
45
45
|
center: { x: hole.x, y: hole.y },
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
radius_x: hole.hole_width / 2,
|
|
47
|
+
radius_y: hole.hole_height / 2,
|
|
48
48
|
fill: colorMap.drill,
|
|
49
49
|
realToCanvasMat,
|
|
50
50
|
})
|
|
@@ -43,8 +43,8 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
43
43
|
drawOval({
|
|
44
44
|
ctx,
|
|
45
45
|
center: { x: hole.x, y: hole.y },
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
radius_x: hole.outer_width / 2,
|
|
47
|
+
radius_y: hole.outer_height / 2,
|
|
48
48
|
fill: colorMap.copper.top,
|
|
49
49
|
realToCanvasMat,
|
|
50
50
|
rotation: hole.ccw_rotation,
|
|
@@ -54,8 +54,8 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
54
54
|
drawOval({
|
|
55
55
|
ctx,
|
|
56
56
|
center: { x: hole.x, y: hole.y },
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
radius_x: hole.hole_width / 2,
|
|
58
|
+
radius_y: hole.hole_height / 2,
|
|
59
59
|
fill: colorMap.drill,
|
|
60
60
|
realToCanvasMat,
|
|
61
61
|
rotation: hole.ccw_rotation,
|
|
@@ -201,8 +201,8 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
201
201
|
drawOval({
|
|
202
202
|
ctx,
|
|
203
203
|
center: { x: holeX, y: holeY },
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
radius_x: (hole.hole_width ?? 0) / 2,
|
|
205
|
+
radius_y: (hole.hole_height ?? 0) / 2,
|
|
206
206
|
fill: colorMap.drill,
|
|
207
207
|
realToCanvasMat,
|
|
208
208
|
})
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { PcbSilkscreenOval } from "circuit-json"
|
|
2
|
+
import type { Matrix } from "transformation-matrix"
|
|
3
|
+
import type { PcbColorMap, CanvasContext } from "../types"
|
|
4
|
+
import { drawOval } from "../shapes/oval"
|
|
5
|
+
|
|
6
|
+
export interface DrawPcbSilkscreenOvalParams {
|
|
7
|
+
ctx: CanvasContext
|
|
8
|
+
oval: PcbSilkscreenOval
|
|
9
|
+
realToCanvasMat: Matrix
|
|
10
|
+
colorMap: PcbColorMap
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function layerToSilkscreenColor(layer: string, colorMap: PcbColorMap): string {
|
|
14
|
+
return layer === "bottom"
|
|
15
|
+
? colorMap.silkscreen.bottom
|
|
16
|
+
: colorMap.silkscreen.top
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function drawPcbSilkscreenOval(
|
|
20
|
+
params: DrawPcbSilkscreenOvalParams,
|
|
21
|
+
): void {
|
|
22
|
+
const { ctx, oval, realToCanvasMat, colorMap } = params
|
|
23
|
+
const color = layerToSilkscreenColor(oval.layer, colorMap)
|
|
24
|
+
|
|
25
|
+
drawOval({
|
|
26
|
+
ctx,
|
|
27
|
+
center: oval.center,
|
|
28
|
+
radius_x: oval.radius_x,
|
|
29
|
+
radius_y: oval.radius_y,
|
|
30
|
+
stroke: color,
|
|
31
|
+
strokeWidth: 0.1,
|
|
32
|
+
realToCanvasMat,
|
|
33
|
+
rotation: oval.ccw_rotation ?? 0,
|
|
34
|
+
})
|
|
35
|
+
}
|
|
@@ -5,6 +5,11 @@ 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
|
+
} from "./soldermask-margin"
|
|
8
13
|
|
|
9
14
|
export interface DrawPcbSmtPadParams {
|
|
10
15
|
ctx: CanvasContext
|
|
@@ -20,12 +25,45 @@ function layerToColor(layer: string, colorMap: PcbColorMap): string {
|
|
|
20
25
|
)
|
|
21
26
|
}
|
|
22
27
|
|
|
28
|
+
function getSoldermaskColor(layer: string, colorMap: PcbColorMap): string {
|
|
29
|
+
return (
|
|
30
|
+
colorMap.soldermaskOverCopper[
|
|
31
|
+
layer as keyof typeof colorMap.soldermaskOverCopper
|
|
32
|
+
] ?? colorMap.soldermaskOverCopper.top
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
24
37
|
const { ctx, pad, realToCanvasMat, colorMap } = params
|
|
25
38
|
|
|
26
39
|
const color = layerToColor(pad.layer, colorMap)
|
|
40
|
+
const hasSoldermask =
|
|
41
|
+
pad.is_covered_with_solder_mask === true &&
|
|
42
|
+
pad.soldermask_margin !== undefined &&
|
|
43
|
+
pad.soldermask_margin !== 0
|
|
44
|
+
const margin = hasSoldermask ? pad.soldermask_margin! : 0
|
|
45
|
+
const soldermaskRingColor = getSoldermaskColor(pad.layer, colorMap)
|
|
46
|
+
const positiveMarginColor = colorMap.substrate
|
|
27
47
|
|
|
48
|
+
// Draw the copper pad
|
|
28
49
|
if (pad.shape === "rect") {
|
|
50
|
+
// For positive margins, draw extended mask area first
|
|
51
|
+
if (hasSoldermask && margin > 0) {
|
|
52
|
+
drawRect({
|
|
53
|
+
ctx,
|
|
54
|
+
center: { x: pad.x, y: pad.y },
|
|
55
|
+
width: pad.width + margin * 2,
|
|
56
|
+
height: pad.height + margin * 2,
|
|
57
|
+
fill: positiveMarginColor,
|
|
58
|
+
realToCanvasMat,
|
|
59
|
+
borderRadius:
|
|
60
|
+
((pad as { corner_radius?: number }).corner_radius ??
|
|
61
|
+
pad.rect_border_radius ??
|
|
62
|
+
0) + margin,
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Draw the pad on top
|
|
29
67
|
drawRect({
|
|
30
68
|
ctx,
|
|
31
69
|
center: { x: pad.x, y: pad.y },
|
|
@@ -38,10 +76,46 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
38
76
|
pad.rect_border_radius ??
|
|
39
77
|
0,
|
|
40
78
|
})
|
|
79
|
+
|
|
80
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
81
|
+
if (hasSoldermask && margin < 0) {
|
|
82
|
+
drawSoldermaskRingForRect(
|
|
83
|
+
ctx,
|
|
84
|
+
{ x: pad.x, y: pad.y },
|
|
85
|
+
pad.width,
|
|
86
|
+
pad.height,
|
|
87
|
+
margin,
|
|
88
|
+
(pad as { corner_radius?: number }).corner_radius ??
|
|
89
|
+
pad.rect_border_radius ??
|
|
90
|
+
0,
|
|
91
|
+
0,
|
|
92
|
+
realToCanvasMat,
|
|
93
|
+
soldermaskRingColor,
|
|
94
|
+
color,
|
|
95
|
+
)
|
|
96
|
+
}
|
|
41
97
|
return
|
|
42
98
|
}
|
|
43
99
|
|
|
44
100
|
if (pad.shape === "rotated_rect") {
|
|
101
|
+
// For positive margins, draw extended mask area first
|
|
102
|
+
if (hasSoldermask && margin > 0) {
|
|
103
|
+
drawRect({
|
|
104
|
+
ctx,
|
|
105
|
+
center: { x: pad.x, y: pad.y },
|
|
106
|
+
width: pad.width + margin * 2,
|
|
107
|
+
height: pad.height + margin * 2,
|
|
108
|
+
fill: positiveMarginColor,
|
|
109
|
+
realToCanvasMat,
|
|
110
|
+
borderRadius:
|
|
111
|
+
((pad as { corner_radius?: number }).corner_radius ??
|
|
112
|
+
pad.rect_border_radius ??
|
|
113
|
+
0) + margin,
|
|
114
|
+
rotation: pad.ccw_rotation ?? 0,
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Draw the pad on top
|
|
45
119
|
drawRect({
|
|
46
120
|
ctx,
|
|
47
121
|
center: { x: pad.x, y: pad.y },
|
|
@@ -55,10 +129,40 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
55
129
|
0,
|
|
56
130
|
rotation: pad.ccw_rotation ?? 0,
|
|
57
131
|
})
|
|
132
|
+
|
|
133
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
134
|
+
if (hasSoldermask && margin < 0) {
|
|
135
|
+
drawSoldermaskRingForRect(
|
|
136
|
+
ctx,
|
|
137
|
+
{ x: pad.x, y: pad.y },
|
|
138
|
+
pad.width,
|
|
139
|
+
pad.height,
|
|
140
|
+
margin,
|
|
141
|
+
(pad as { corner_radius?: number }).corner_radius ??
|
|
142
|
+
pad.rect_border_radius ??
|
|
143
|
+
0,
|
|
144
|
+
pad.ccw_rotation ?? 0,
|
|
145
|
+
realToCanvasMat,
|
|
146
|
+
soldermaskRingColor,
|
|
147
|
+
color,
|
|
148
|
+
)
|
|
149
|
+
}
|
|
58
150
|
return
|
|
59
151
|
}
|
|
60
152
|
|
|
61
153
|
if (pad.shape === "circle") {
|
|
154
|
+
// For positive margins, draw extended mask area first
|
|
155
|
+
if (hasSoldermask && margin > 0) {
|
|
156
|
+
drawCircle({
|
|
157
|
+
ctx,
|
|
158
|
+
center: { x: pad.x, y: pad.y },
|
|
159
|
+
radius: pad.radius + margin,
|
|
160
|
+
fill: positiveMarginColor,
|
|
161
|
+
realToCanvasMat,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Draw the pad on top
|
|
62
166
|
drawCircle({
|
|
63
167
|
ctx,
|
|
64
168
|
center: { x: pad.x, y: pad.y },
|
|
@@ -66,10 +170,36 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
66
170
|
fill: color,
|
|
67
171
|
realToCanvasMat,
|
|
68
172
|
})
|
|
173
|
+
|
|
174
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
175
|
+
if (hasSoldermask && margin < 0) {
|
|
176
|
+
drawSoldermaskRingForCircle(
|
|
177
|
+
ctx,
|
|
178
|
+
{ x: pad.x, y: pad.y },
|
|
179
|
+
pad.radius,
|
|
180
|
+
margin,
|
|
181
|
+
realToCanvasMat,
|
|
182
|
+
soldermaskRingColor,
|
|
183
|
+
color,
|
|
184
|
+
)
|
|
185
|
+
}
|
|
69
186
|
return
|
|
70
187
|
}
|
|
71
188
|
|
|
72
189
|
if (pad.shape === "pill") {
|
|
190
|
+
// For positive margins, draw extended mask area first
|
|
191
|
+
if (hasSoldermask && margin > 0) {
|
|
192
|
+
drawPill({
|
|
193
|
+
ctx,
|
|
194
|
+
center: { x: pad.x, y: pad.y },
|
|
195
|
+
width: pad.width + margin * 2,
|
|
196
|
+
height: pad.height + margin * 2,
|
|
197
|
+
fill: positiveMarginColor,
|
|
198
|
+
realToCanvasMat,
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Draw the pad on top
|
|
73
203
|
drawPill({
|
|
74
204
|
ctx,
|
|
75
205
|
center: { x: pad.x, y: pad.y },
|
|
@@ -78,10 +208,39 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
78
208
|
fill: color,
|
|
79
209
|
realToCanvasMat,
|
|
80
210
|
})
|
|
211
|
+
|
|
212
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
213
|
+
if (hasSoldermask && margin < 0) {
|
|
214
|
+
drawSoldermaskRingForPill(
|
|
215
|
+
ctx,
|
|
216
|
+
{ x: pad.x, y: pad.y },
|
|
217
|
+
pad.width,
|
|
218
|
+
pad.height,
|
|
219
|
+
margin,
|
|
220
|
+
0,
|
|
221
|
+
realToCanvasMat,
|
|
222
|
+
soldermaskRingColor,
|
|
223
|
+
color,
|
|
224
|
+
)
|
|
225
|
+
}
|
|
81
226
|
return
|
|
82
227
|
}
|
|
83
228
|
|
|
84
229
|
if (pad.shape === "rotated_pill") {
|
|
230
|
+
// For positive margins, draw extended mask area first
|
|
231
|
+
if (hasSoldermask && margin > 0) {
|
|
232
|
+
drawPill({
|
|
233
|
+
ctx,
|
|
234
|
+
center: { x: pad.x, y: pad.y },
|
|
235
|
+
width: pad.width + margin * 2,
|
|
236
|
+
height: pad.height + margin * 2,
|
|
237
|
+
fill: positiveMarginColor,
|
|
238
|
+
realToCanvasMat,
|
|
239
|
+
rotation: pad.ccw_rotation ?? 0,
|
|
240
|
+
})
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Draw the pad on top
|
|
85
244
|
drawPill({
|
|
86
245
|
ctx,
|
|
87
246
|
center: { x: pad.x, y: pad.y },
|
|
@@ -91,6 +250,21 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
91
250
|
realToCanvasMat,
|
|
92
251
|
rotation: pad.ccw_rotation ?? 0,
|
|
93
252
|
})
|
|
253
|
+
|
|
254
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
255
|
+
if (hasSoldermask && margin < 0) {
|
|
256
|
+
drawSoldermaskRingForPill(
|
|
257
|
+
ctx,
|
|
258
|
+
{ x: pad.x, y: pad.y },
|
|
259
|
+
pad.width,
|
|
260
|
+
pad.height,
|
|
261
|
+
margin,
|
|
262
|
+
pad.ccw_rotation ?? 0,
|
|
263
|
+
realToCanvasMat,
|
|
264
|
+
soldermaskRingColor,
|
|
265
|
+
color,
|
|
266
|
+
)
|
|
267
|
+
}
|
|
94
268
|
return
|
|
95
269
|
}
|
|
96
270
|
|