circuit-to-canvas 0.0.46 → 0.0.47
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 +136 -6
- package/lib/drawer/elements/pcb-plated-hole.ts +40 -5
- package/lib/drawer/elements/pcb-smtpad.ts +29 -4
- package/lib/drawer/elements/soldermask-margin.ts +130 -0
- package/package.json +1 -1
- package/tests/elements/__snapshots__/pcb-comprehensive-soldermask-margin.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-plated-hole-soldermask-margin.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-smtpad-soldermask-margin.snap.png +0 -0
- package/tests/elements/pcb-comprehensive-soldermask-margin.test.ts +1034 -83
package/dist/index.js
CHANGED
|
@@ -456,6 +456,90 @@ function drawSoldermaskRingForOval(ctx, center, radius_x, radius_y, margin, rota
|
|
|
456
456
|
}
|
|
457
457
|
ctx.restore();
|
|
458
458
|
}
|
|
459
|
+
function offsetPolygonPoints(points, offset) {
|
|
460
|
+
if (points.length < 3 || offset === 0) return points;
|
|
461
|
+
let centerX = 0;
|
|
462
|
+
let centerY = 0;
|
|
463
|
+
for (const point of points) {
|
|
464
|
+
centerX += point.x;
|
|
465
|
+
centerY += point.y;
|
|
466
|
+
}
|
|
467
|
+
centerX /= points.length;
|
|
468
|
+
centerY /= points.length;
|
|
469
|
+
const result = [];
|
|
470
|
+
for (const point of points) {
|
|
471
|
+
const vectorX = point.x - centerX;
|
|
472
|
+
const vectorY = point.y - centerY;
|
|
473
|
+
const distance = Math.sqrt(vectorX * vectorX + vectorY * vectorY);
|
|
474
|
+
if (distance > 0) {
|
|
475
|
+
const normalizedX = vectorX / distance;
|
|
476
|
+
const normalizedY = vectorY / distance;
|
|
477
|
+
result.push({
|
|
478
|
+
x: point.x + normalizedX * offset,
|
|
479
|
+
y: point.y + normalizedY * offset
|
|
480
|
+
});
|
|
481
|
+
} else {
|
|
482
|
+
result.push({
|
|
483
|
+
x: point.x + offset,
|
|
484
|
+
y: point.y
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
function drawSoldermaskRingForPolygon(ctx, points, margin, realToCanvasMat, soldermaskColor, padColor) {
|
|
491
|
+
if (points.length < 3 || margin >= 0) return;
|
|
492
|
+
const scaledMargin = Math.abs(margin) * Math.abs(realToCanvasMat.a);
|
|
493
|
+
const prevCompositeOp = ctx.globalCompositeOperation;
|
|
494
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
495
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
496
|
+
}
|
|
497
|
+
ctx.beginPath();
|
|
498
|
+
const canvasPoints = points.map(
|
|
499
|
+
(p) => applyToPoint6(realToCanvasMat, [p.x, p.y])
|
|
500
|
+
);
|
|
501
|
+
const firstPoint = canvasPoints[0];
|
|
502
|
+
if (firstPoint) {
|
|
503
|
+
const [firstX, firstY] = firstPoint;
|
|
504
|
+
ctx.moveTo(firstX, firstY);
|
|
505
|
+
for (let i = 1; i < canvasPoints.length; i++) {
|
|
506
|
+
const point = canvasPoints[i];
|
|
507
|
+
if (point) {
|
|
508
|
+
const [x, y] = point;
|
|
509
|
+
ctx.lineTo(x, y);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
ctx.closePath();
|
|
513
|
+
ctx.fillStyle = soldermaskColor;
|
|
514
|
+
ctx.fill();
|
|
515
|
+
}
|
|
516
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
517
|
+
ctx.globalCompositeOperation = prevCompositeOp || "source-over";
|
|
518
|
+
}
|
|
519
|
+
const innerPoints = offsetPolygonPoints(points, margin);
|
|
520
|
+
if (innerPoints.length >= 3) {
|
|
521
|
+
ctx.beginPath();
|
|
522
|
+
const innerCanvasPoints = innerPoints.map(
|
|
523
|
+
(p) => applyToPoint6(realToCanvasMat, [p.x, p.y])
|
|
524
|
+
);
|
|
525
|
+
const firstInnerPoint = innerCanvasPoints[0];
|
|
526
|
+
if (firstInnerPoint) {
|
|
527
|
+
const [firstX, firstY] = firstInnerPoint;
|
|
528
|
+
ctx.moveTo(firstX, firstY);
|
|
529
|
+
for (let i = 1; i < innerCanvasPoints.length; i++) {
|
|
530
|
+
const point = innerCanvasPoints[i];
|
|
531
|
+
if (point) {
|
|
532
|
+
const [x, y] = point;
|
|
533
|
+
ctx.lineTo(x, y);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
ctx.closePath();
|
|
537
|
+
ctx.fillStyle = padColor;
|
|
538
|
+
ctx.fill();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
ctx.restore();
|
|
542
|
+
}
|
|
459
543
|
|
|
460
544
|
// lib/drawer/elements/pcb-plated-hole.ts
|
|
461
545
|
function getSoldermaskColor(layers, colorMap) {
|
|
@@ -643,7 +727,7 @@ function drawPcbPlatedHole(params) {
|
|
|
643
727
|
height: hole.rect_pad_height + margin * 2,
|
|
644
728
|
fill: positiveMarginColor,
|
|
645
729
|
realToCanvasMat,
|
|
646
|
-
borderRadius:
|
|
730
|
+
borderRadius: hole.rect_border_radius ?? 0
|
|
647
731
|
});
|
|
648
732
|
}
|
|
649
733
|
drawRect({
|
|
@@ -702,7 +786,7 @@ function drawPcbPlatedHole(params) {
|
|
|
702
786
|
height: hole.rect_pad_height + margin * 2,
|
|
703
787
|
fill: positiveMarginColor,
|
|
704
788
|
realToCanvasMat,
|
|
705
|
-
borderRadius:
|
|
789
|
+
borderRadius: hole.rect_border_radius ?? 0
|
|
706
790
|
});
|
|
707
791
|
}
|
|
708
792
|
drawRect({
|
|
@@ -762,7 +846,7 @@ function drawPcbPlatedHole(params) {
|
|
|
762
846
|
height: hole.rect_pad_height + margin * 2,
|
|
763
847
|
fill: positiveMarginColor,
|
|
764
848
|
realToCanvasMat,
|
|
765
|
-
borderRadius:
|
|
849
|
+
borderRadius: hole.rect_border_radius ?? 0,
|
|
766
850
|
rotation: hole.rect_ccw_rotation
|
|
767
851
|
});
|
|
768
852
|
}
|
|
@@ -824,12 +908,39 @@ function drawPcbPlatedHole(params) {
|
|
|
824
908
|
x: hole.x + point.x,
|
|
825
909
|
y: hole.y + point.y
|
|
826
910
|
}));
|
|
911
|
+
if (hasSoldermask && margin > 0) {
|
|
912
|
+
const expandedPoints = offsetPolygonPoints(padPoints, margin);
|
|
913
|
+
drawPolygon({
|
|
914
|
+
ctx,
|
|
915
|
+
points: expandedPoints,
|
|
916
|
+
fill: positiveMarginColor,
|
|
917
|
+
realToCanvasMat
|
|
918
|
+
});
|
|
919
|
+
}
|
|
827
920
|
drawPolygon({
|
|
828
921
|
ctx,
|
|
829
922
|
points: padPoints,
|
|
830
923
|
fill: copperColor,
|
|
831
924
|
realToCanvasMat
|
|
832
925
|
});
|
|
926
|
+
if (hasSoldermask && margin < 0) {
|
|
927
|
+
drawSoldermaskRingForPolygon(
|
|
928
|
+
ctx,
|
|
929
|
+
padPoints,
|
|
930
|
+
margin,
|
|
931
|
+
realToCanvasMat,
|
|
932
|
+
soldermaskRingColor,
|
|
933
|
+
copperColor
|
|
934
|
+
);
|
|
935
|
+
}
|
|
936
|
+
if (isCoveredWithSoldermask) {
|
|
937
|
+
drawPolygon({
|
|
938
|
+
ctx,
|
|
939
|
+
points: padPoints,
|
|
940
|
+
fill: soldermaskRingColor,
|
|
941
|
+
realToCanvasMat
|
|
942
|
+
});
|
|
943
|
+
}
|
|
833
944
|
}
|
|
834
945
|
if (!isCoveredWithSoldermask) {
|
|
835
946
|
const holeX = hole.x + (hole.hole_offset_x ?? 0);
|
|
@@ -1233,7 +1344,7 @@ function drawPcbSmtPad(params) {
|
|
|
1233
1344
|
height: pad.height + margin * 2,
|
|
1234
1345
|
fill: positiveMarginColor,
|
|
1235
1346
|
realToCanvasMat,
|
|
1236
|
-
borderRadius: getBorderRadius(pad
|
|
1347
|
+
borderRadius: getBorderRadius(pad)
|
|
1237
1348
|
});
|
|
1238
1349
|
}
|
|
1239
1350
|
drawRect({
|
|
@@ -1281,7 +1392,7 @@ function drawPcbSmtPad(params) {
|
|
|
1281
1392
|
height: pad.height + margin * 2,
|
|
1282
1393
|
fill: positiveMarginColor,
|
|
1283
1394
|
realToCanvasMat,
|
|
1284
|
-
borderRadius: getBorderRadius(pad
|
|
1395
|
+
borderRadius: getBorderRadius(pad),
|
|
1285
1396
|
rotation: pad.ccw_rotation ?? 0
|
|
1286
1397
|
});
|
|
1287
1398
|
}
|
|
@@ -1455,13 +1566,32 @@ function drawPcbSmtPad(params) {
|
|
|
1455
1566
|
}
|
|
1456
1567
|
if (pad.shape === "polygon") {
|
|
1457
1568
|
if (pad.points && pad.points.length >= 3) {
|
|
1569
|
+
if (hasSoldermask && margin > 0) {
|
|
1570
|
+
const expandedPoints = offsetPolygonPoints(pad.points, margin);
|
|
1571
|
+
drawPolygon({
|
|
1572
|
+
ctx,
|
|
1573
|
+
points: expandedPoints,
|
|
1574
|
+
fill: positiveMarginColor,
|
|
1575
|
+
realToCanvasMat
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1458
1578
|
drawPolygon({
|
|
1459
1579
|
ctx,
|
|
1460
1580
|
points: pad.points,
|
|
1461
1581
|
fill: color,
|
|
1462
1582
|
realToCanvasMat
|
|
1463
1583
|
});
|
|
1464
|
-
if (
|
|
1584
|
+
if (hasSoldermask && margin < 0) {
|
|
1585
|
+
drawSoldermaskRingForPolygon(
|
|
1586
|
+
ctx,
|
|
1587
|
+
pad.points,
|
|
1588
|
+
margin,
|
|
1589
|
+
realToCanvasMat,
|
|
1590
|
+
soldermaskRingColor,
|
|
1591
|
+
color
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
if (isCoveredWithSoldermask && margin === 0) {
|
|
1465
1595
|
drawPolygon({
|
|
1466
1596
|
ctx,
|
|
1467
1597
|
points: pad.points,
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
drawSoldermaskRingForOval,
|
|
12
12
|
drawSoldermaskRingForPill,
|
|
13
13
|
drawSoldermaskRingForRect,
|
|
14
|
+
drawSoldermaskRingForPolygon,
|
|
15
|
+
offsetPolygonPoints,
|
|
14
16
|
} from "./soldermask-margin"
|
|
15
17
|
|
|
16
18
|
export interface DrawPcbPlatedHoleParams {
|
|
@@ -249,7 +251,7 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
249
251
|
height: hole.rect_pad_height + margin * 2,
|
|
250
252
|
fill: positiveMarginColor,
|
|
251
253
|
realToCanvasMat,
|
|
252
|
-
borderRadius:
|
|
254
|
+
borderRadius: hole.rect_border_radius ?? 0,
|
|
253
255
|
})
|
|
254
256
|
}
|
|
255
257
|
|
|
@@ -318,7 +320,7 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
318
320
|
height: hole.rect_pad_height + margin * 2,
|
|
319
321
|
fill: positiveMarginColor,
|
|
320
322
|
realToCanvasMat,
|
|
321
|
-
borderRadius:
|
|
323
|
+
borderRadius: hole.rect_border_radius ?? 0,
|
|
322
324
|
})
|
|
323
325
|
}
|
|
324
326
|
|
|
@@ -388,7 +390,7 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
388
390
|
height: hole.rect_pad_height + margin * 2,
|
|
389
391
|
fill: positiveMarginColor,
|
|
390
392
|
realToCanvasMat,
|
|
391
|
-
borderRadius:
|
|
393
|
+
borderRadius: hole.rect_border_radius ?? 0,
|
|
392
394
|
rotation: hole.rect_ccw_rotation,
|
|
393
395
|
})
|
|
394
396
|
}
|
|
@@ -453,8 +455,6 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
453
455
|
}
|
|
454
456
|
|
|
455
457
|
if (hole.shape === "hole_with_polygon_pad") {
|
|
456
|
-
// Note: Polygon pads don't support soldermask margins (similar to SMT polygon pads)
|
|
457
|
-
// Draw polygon pad
|
|
458
458
|
const padOutline = hole.pad_outline
|
|
459
459
|
if (padOutline && padOutline.length >= 3) {
|
|
460
460
|
// Transform pad_outline points to be relative to hole.x, hole.y
|
|
@@ -462,12 +462,47 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
|
|
|
462
462
|
x: hole.x + point.x,
|
|
463
463
|
y: hole.y + point.y,
|
|
464
464
|
}))
|
|
465
|
+
|
|
466
|
+
// For positive margins, draw extended mask area first
|
|
467
|
+
if (hasSoldermask && margin > 0) {
|
|
468
|
+
const expandedPoints = offsetPolygonPoints(padPoints, margin)
|
|
469
|
+
drawPolygon({
|
|
470
|
+
ctx,
|
|
471
|
+
points: expandedPoints,
|
|
472
|
+
fill: positiveMarginColor,
|
|
473
|
+
realToCanvasMat,
|
|
474
|
+
})
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Draw polygon pad
|
|
465
478
|
drawPolygon({
|
|
466
479
|
ctx,
|
|
467
480
|
points: padPoints,
|
|
468
481
|
fill: copperColor,
|
|
469
482
|
realToCanvasMat,
|
|
470
483
|
})
|
|
484
|
+
|
|
485
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
486
|
+
if (hasSoldermask && margin < 0) {
|
|
487
|
+
drawSoldermaskRingForPolygon(
|
|
488
|
+
ctx,
|
|
489
|
+
padPoints,
|
|
490
|
+
margin,
|
|
491
|
+
realToCanvasMat,
|
|
492
|
+
soldermaskRingColor,
|
|
493
|
+
copperColor,
|
|
494
|
+
)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// If fully covered, draw soldermask overlay
|
|
498
|
+
if (isCoveredWithSoldermask) {
|
|
499
|
+
drawPolygon({
|
|
500
|
+
ctx,
|
|
501
|
+
points: padPoints,
|
|
502
|
+
fill: soldermaskRingColor,
|
|
503
|
+
realToCanvasMat,
|
|
504
|
+
})
|
|
505
|
+
}
|
|
471
506
|
}
|
|
472
507
|
|
|
473
508
|
// Draw drill hole (with offset, only if not fully covered with soldermask)
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
drawSoldermaskRingForRect,
|
|
10
10
|
drawSoldermaskRingForCircle,
|
|
11
11
|
drawSoldermaskRingForPill,
|
|
12
|
+
drawSoldermaskRingForPolygon,
|
|
13
|
+
offsetPolygonPoints,
|
|
12
14
|
} from "./soldermask-margin"
|
|
13
15
|
|
|
14
16
|
export interface DrawPcbSmtPadParams {
|
|
@@ -67,7 +69,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
67
69
|
height: pad.height + margin * 2,
|
|
68
70
|
fill: positiveMarginColor,
|
|
69
71
|
realToCanvasMat,
|
|
70
|
-
borderRadius: getBorderRadius(pad
|
|
72
|
+
borderRadius: getBorderRadius(pad),
|
|
71
73
|
})
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -123,7 +125,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
123
125
|
height: pad.height + margin * 2,
|
|
124
126
|
fill: positiveMarginColor,
|
|
125
127
|
realToCanvasMat,
|
|
126
|
-
borderRadius: getBorderRadius(pad
|
|
128
|
+
borderRadius: getBorderRadius(pad),
|
|
127
129
|
rotation: pad.ccw_rotation ?? 0,
|
|
128
130
|
})
|
|
129
131
|
}
|
|
@@ -328,6 +330,17 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
328
330
|
|
|
329
331
|
if (pad.shape === "polygon") {
|
|
330
332
|
if (pad.points && pad.points.length >= 3) {
|
|
333
|
+
// For positive margins, draw extended mask area first
|
|
334
|
+
if (hasSoldermask && margin > 0) {
|
|
335
|
+
const expandedPoints = offsetPolygonPoints(pad.points, margin)
|
|
336
|
+
drawPolygon({
|
|
337
|
+
ctx,
|
|
338
|
+
points: expandedPoints,
|
|
339
|
+
fill: positiveMarginColor,
|
|
340
|
+
realToCanvasMat,
|
|
341
|
+
})
|
|
342
|
+
}
|
|
343
|
+
|
|
331
344
|
// Draw the copper pad
|
|
332
345
|
drawPolygon({
|
|
333
346
|
ctx,
|
|
@@ -336,8 +349,20 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
336
349
|
realToCanvasMat,
|
|
337
350
|
})
|
|
338
351
|
|
|
339
|
-
//
|
|
340
|
-
if (
|
|
352
|
+
// For negative margins, draw soldermask ring on top of the pad
|
|
353
|
+
if (hasSoldermask && margin < 0) {
|
|
354
|
+
drawSoldermaskRingForPolygon(
|
|
355
|
+
ctx,
|
|
356
|
+
pad.points,
|
|
357
|
+
margin,
|
|
358
|
+
realToCanvasMat,
|
|
359
|
+
soldermaskRingColor,
|
|
360
|
+
color,
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// If covered with soldermask and margin == 0 (treat as 0 positive margin), draw soldermaskOverCopper overlay
|
|
365
|
+
if (isCoveredWithSoldermask && margin === 0) {
|
|
341
366
|
drawPolygon({
|
|
342
367
|
ctx,
|
|
343
368
|
points: pad.points,
|
|
@@ -324,3 +324,133 @@ export function drawSoldermaskRingForOval(
|
|
|
324
324
|
|
|
325
325
|
ctx.restore()
|
|
326
326
|
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Offsets polygon points by a given distance (positive = outward, negative = inward)
|
|
330
|
+
*/
|
|
331
|
+
export function offsetPolygonPoints(
|
|
332
|
+
points: Array<{ x: number; y: number }>,
|
|
333
|
+
offset: number,
|
|
334
|
+
): Array<{ x: number; y: number }> {
|
|
335
|
+
if (points.length < 3 || offset === 0) return points
|
|
336
|
+
|
|
337
|
+
// Calculate polygon centroid
|
|
338
|
+
let centerX = 0
|
|
339
|
+
let centerY = 0
|
|
340
|
+
for (const point of points) {
|
|
341
|
+
centerX += point.x
|
|
342
|
+
centerY += point.y
|
|
343
|
+
}
|
|
344
|
+
centerX /= points.length
|
|
345
|
+
centerY /= points.length
|
|
346
|
+
|
|
347
|
+
const result: Array<{ x: number; y: number }> = []
|
|
348
|
+
|
|
349
|
+
for (const point of points) {
|
|
350
|
+
// Calculate vector from center to point
|
|
351
|
+
const vectorX = point.x - centerX
|
|
352
|
+
const vectorY = point.y - centerY
|
|
353
|
+
|
|
354
|
+
// Calculate distance from center
|
|
355
|
+
const distance = Math.sqrt(vectorX * vectorX + vectorY * vectorY)
|
|
356
|
+
|
|
357
|
+
if (distance > 0) {
|
|
358
|
+
// Normalize vector and offset
|
|
359
|
+
const normalizedX = vectorX / distance
|
|
360
|
+
const normalizedY = vectorY / distance
|
|
361
|
+
|
|
362
|
+
result.push({
|
|
363
|
+
x: point.x + normalizedX * offset,
|
|
364
|
+
y: point.y + normalizedY * offset,
|
|
365
|
+
})
|
|
366
|
+
} else {
|
|
367
|
+
// Point is at center, offset in arbitrary direction
|
|
368
|
+
result.push({
|
|
369
|
+
x: point.x + offset,
|
|
370
|
+
y: point.y,
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return result
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Draws a soldermask ring for polygon shapes with negative margin
|
|
380
|
+
* (soldermask appears inside the pad boundary)
|
|
381
|
+
*/
|
|
382
|
+
export function drawSoldermaskRingForPolygon(
|
|
383
|
+
ctx: CanvasContext,
|
|
384
|
+
points: Array<{ x: number; y: number }>,
|
|
385
|
+
margin: number,
|
|
386
|
+
realToCanvasMat: Matrix,
|
|
387
|
+
soldermaskColor: string,
|
|
388
|
+
padColor: string,
|
|
389
|
+
): void {
|
|
390
|
+
if (points.length < 3 || margin >= 0) return
|
|
391
|
+
|
|
392
|
+
const scaledMargin = Math.abs(margin) * Math.abs(realToCanvasMat.a)
|
|
393
|
+
|
|
394
|
+
// For negative margins, outer is pad boundary, inner is contracted by margin
|
|
395
|
+
// Use source-atop so the ring only appears on the pad
|
|
396
|
+
const prevCompositeOp = ctx.globalCompositeOperation
|
|
397
|
+
if (ctx.globalCompositeOperation !== undefined) {
|
|
398
|
+
ctx.globalCompositeOperation = "source-atop"
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Draw outer polygon filled (at pad boundary)
|
|
402
|
+
ctx.beginPath()
|
|
403
|
+
const canvasPoints = points.map((p) =>
|
|
404
|
+
applyToPoint(realToCanvasMat, [p.x, p.y]),
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
const firstPoint = canvasPoints[0]
|
|
408
|
+
if (firstPoint) {
|
|
409
|
+
const [firstX, firstY] = firstPoint
|
|
410
|
+
ctx.moveTo(firstX, firstY)
|
|
411
|
+
|
|
412
|
+
for (let i = 1; i < canvasPoints.length; i++) {
|
|
413
|
+
const point = canvasPoints[i]
|
|
414
|
+
if (point) {
|
|
415
|
+
const [x, y] = point
|
|
416
|
+
ctx.lineTo(x, y)
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
ctx.closePath()
|
|
420
|
+
ctx.fillStyle = soldermaskColor
|
|
421
|
+
ctx.fill()
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Reset composite operation and restore pad color in inner area
|
|
425
|
+
if (ctx.globalCompositeOperation !== undefined) {
|
|
426
|
+
ctx.globalCompositeOperation = prevCompositeOp || "source-over"
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Restore pad color in inner polygon (contracted by margin)
|
|
430
|
+
const innerPoints = offsetPolygonPoints(points, margin)
|
|
431
|
+
if (innerPoints.length >= 3) {
|
|
432
|
+
ctx.beginPath()
|
|
433
|
+
const innerCanvasPoints = innerPoints.map((p) =>
|
|
434
|
+
applyToPoint(realToCanvasMat, [p.x, p.y]),
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
const firstInnerPoint = innerCanvasPoints[0]
|
|
438
|
+
if (firstInnerPoint) {
|
|
439
|
+
const [firstX, firstY] = firstInnerPoint
|
|
440
|
+
ctx.moveTo(firstX, firstY)
|
|
441
|
+
|
|
442
|
+
for (let i = 1; i < innerCanvasPoints.length; i++) {
|
|
443
|
+
const point = innerCanvasPoints[i]
|
|
444
|
+
if (point) {
|
|
445
|
+
const [x, y] = point
|
|
446
|
+
ctx.lineTo(x, y)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
ctx.closePath()
|
|
450
|
+
ctx.fillStyle = padColor
|
|
451
|
+
ctx.fill()
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
ctx.restore()
|
|
456
|
+
}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|