circuit-to-canvas 0.0.35 → 0.0.37
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 +9 -1
- package/dist/index.js +158 -61
- package/lib/drawer/CircuitToCanvasDrawer.ts +12 -3
- package/lib/drawer/elements/index.ts +1 -0
- package/lib/drawer/elements/pcb-hole.ts +88 -1
- package/lib/drawer/elements/soldermask-margin.ts +60 -0
- package/lib/drawer/shapes/dimension-line.ts +74 -79
- package/package.json +1 -1
- 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/pcb-hole-soldermask-margin.test.ts +133 -0
package/dist/index.d.ts
CHANGED
|
@@ -341,6 +341,14 @@ declare function drawSoldermaskRingForPill(ctx: CanvasContext, center: {
|
|
|
341
341
|
x: number;
|
|
342
342
|
y: number;
|
|
343
343
|
}, width: number, height: number, margin: number, rotation: number, realToCanvasMat: Matrix, soldermaskColor: string, padColor: string): void;
|
|
344
|
+
/**
|
|
345
|
+
* Draws a soldermask ring for oval shapes with negative margin
|
|
346
|
+
* (soldermask appears inside the hole boundary)
|
|
347
|
+
*/
|
|
348
|
+
declare function drawSoldermaskRingForOval(ctx: CanvasContext, center: {
|
|
349
|
+
x: number;
|
|
350
|
+
y: number;
|
|
351
|
+
}, radius_x: number, radius_y: number, margin: number, rotation: number, realToCanvasMat: Matrix, soldermaskColor: string, holeColor: string): void;
|
|
344
352
|
|
|
345
353
|
interface DrawPcbTraceParams {
|
|
346
354
|
ctx: CanvasContext;
|
|
@@ -502,4 +510,4 @@ interface DrawPcbFabricationNoteDimensionParams {
|
|
|
502
510
|
}
|
|
503
511
|
declare function drawPcbFabricationNoteDimension(params: DrawPcbFabricationNoteDimensionParams): void;
|
|
504
512
|
|
|
505
|
-
export { type AlphabetLayout, type AnchorAlignment, type CameraBounds, type CanvasContext, CircuitToCanvasDrawer, type CopperColorMap, type CopperLayerName, DEFAULT_PCB_COLOR_MAP, type DrawArrowParams, type DrawCircleParams, type DrawContext, type DrawDimensionLineParams, type DrawElementsOptions, type DrawLineParams, type DrawOvalParams, type DrawPathParams, type DrawPcbBoardParams, type DrawPcbCopperPourParams, type DrawPcbCopperTextParams, type DrawPcbCutoutParams, type DrawPcbFabricationNoteDimensionParams, type DrawPcbFabricationNotePathParams, type DrawPcbFabricationNoteRectParams, type DrawPcbFabricationNoteTextParams, type DrawPcbHoleParams, type DrawPcbNoteDimensionParams, type DrawPcbNotePathParams, type DrawPcbNoteRectParams, type DrawPcbNoteTextParams, type DrawPcbPlatedHoleParams, type DrawPcbSilkscreenCircleParams, type DrawPcbSilkscreenLineParams, type DrawPcbSilkscreenOvalParams, type DrawPcbSilkscreenPathParams, type DrawPcbSilkscreenPillParams, type DrawPcbSilkscreenRectParams, type DrawPcbSilkscreenTextParams, type DrawPcbSmtPadParams, type DrawPcbTraceParams, type DrawPcbViaParams, type DrawPillParams, type DrawPolygonParams, type DrawRectParams, type DrawTextParams, type DrawerConfig, type PcbColorMap, drawArrow, drawCircle, drawDimensionLine, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCopperText, drawPcbCutout, drawPcbFabricationNoteDimension, drawPcbFabricationNotePath, drawPcbFabricationNoteRect, drawPcbFabricationNoteText, drawPcbHole, drawPcbNoteDimension, drawPcbNotePath, drawPcbNoteRect, drawPcbNoteText, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenOval, drawPcbSilkscreenPath, drawPcbSilkscreenPill, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect, drawSoldermaskRingForCircle, drawSoldermaskRingForPill, drawSoldermaskRingForRect, drawText, getAlphabetLayout, getTextStartPosition, strokeAlphabetText };
|
|
513
|
+
export { type AlphabetLayout, type AnchorAlignment, type CameraBounds, type CanvasContext, CircuitToCanvasDrawer, type CopperColorMap, type CopperLayerName, DEFAULT_PCB_COLOR_MAP, type DrawArrowParams, type DrawCircleParams, type DrawContext, type DrawDimensionLineParams, type DrawElementsOptions, type DrawLineParams, type DrawOvalParams, type DrawPathParams, type DrawPcbBoardParams, type DrawPcbCopperPourParams, type DrawPcbCopperTextParams, type DrawPcbCutoutParams, type DrawPcbFabricationNoteDimensionParams, type DrawPcbFabricationNotePathParams, type DrawPcbFabricationNoteRectParams, type DrawPcbFabricationNoteTextParams, type DrawPcbHoleParams, type DrawPcbNoteDimensionParams, type DrawPcbNotePathParams, type DrawPcbNoteRectParams, type DrawPcbNoteTextParams, type DrawPcbPlatedHoleParams, type DrawPcbSilkscreenCircleParams, type DrawPcbSilkscreenLineParams, type DrawPcbSilkscreenOvalParams, type DrawPcbSilkscreenPathParams, type DrawPcbSilkscreenPillParams, type DrawPcbSilkscreenRectParams, type DrawPcbSilkscreenTextParams, type DrawPcbSmtPadParams, type DrawPcbTraceParams, type DrawPcbViaParams, type DrawPillParams, type DrawPolygonParams, type DrawRectParams, type DrawTextParams, type DrawerConfig, type PcbColorMap, drawArrow, drawCircle, drawDimensionLine, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCopperText, drawPcbCutout, drawPcbFabricationNoteDimension, drawPcbFabricationNotePath, drawPcbFabricationNoteRect, drawPcbFabricationNoteText, drawPcbHole, drawPcbNoteDimension, drawPcbNotePath, drawPcbNoteRect, drawPcbNoteText, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenOval, drawPcbSilkscreenPath, drawPcbSilkscreenPill, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect, drawSoldermaskRingForCircle, drawSoldermaskRingForOval, drawSoldermaskRingForPill, drawSoldermaskRingForRect, drawText, getAlphabetLayout, getTextStartPosition, strokeAlphabetText };
|
package/dist/index.js
CHANGED
|
@@ -457,7 +457,19 @@ function drawPcbVia(params) {
|
|
|
457
457
|
// lib/drawer/elements/pcb-hole.ts
|
|
458
458
|
function drawPcbHole(params) {
|
|
459
459
|
const { ctx, hole, realToCanvasMat, colorMap } = params;
|
|
460
|
+
const hasSoldermask = hole.is_covered_with_solder_mask === true && hole.soldermask_margin !== void 0 && hole.soldermask_margin > 0;
|
|
461
|
+
const margin = hasSoldermask ? hole.soldermask_margin : 0;
|
|
462
|
+
const positiveMarginColor = colorMap.substrate;
|
|
460
463
|
if (hole.hole_shape === "circle") {
|
|
464
|
+
if (hasSoldermask && margin > 0) {
|
|
465
|
+
drawCircle({
|
|
466
|
+
ctx,
|
|
467
|
+
center: { x: hole.x, y: hole.y },
|
|
468
|
+
radius: hole.hole_diameter / 2 + margin,
|
|
469
|
+
fill: positiveMarginColor,
|
|
470
|
+
realToCanvasMat
|
|
471
|
+
});
|
|
472
|
+
}
|
|
461
473
|
drawCircle({
|
|
462
474
|
ctx,
|
|
463
475
|
center: { x: hole.x, y: hole.y },
|
|
@@ -468,6 +480,16 @@ function drawPcbHole(params) {
|
|
|
468
480
|
return;
|
|
469
481
|
}
|
|
470
482
|
if (hole.hole_shape === "square") {
|
|
483
|
+
if (hasSoldermask && margin > 0) {
|
|
484
|
+
drawRect({
|
|
485
|
+
ctx,
|
|
486
|
+
center: { x: hole.x, y: hole.y },
|
|
487
|
+
width: hole.hole_diameter + margin * 2,
|
|
488
|
+
height: hole.hole_diameter + margin * 2,
|
|
489
|
+
fill: positiveMarginColor,
|
|
490
|
+
realToCanvasMat
|
|
491
|
+
});
|
|
492
|
+
}
|
|
471
493
|
drawRect({
|
|
472
494
|
ctx,
|
|
473
495
|
center: { x: hole.x, y: hole.y },
|
|
@@ -479,6 +501,16 @@ function drawPcbHole(params) {
|
|
|
479
501
|
return;
|
|
480
502
|
}
|
|
481
503
|
if (hole.hole_shape === "oval") {
|
|
504
|
+
if (hasSoldermask && margin > 0) {
|
|
505
|
+
drawOval({
|
|
506
|
+
ctx,
|
|
507
|
+
center: { x: hole.x, y: hole.y },
|
|
508
|
+
radius_x: hole.hole_width / 2 + margin,
|
|
509
|
+
radius_y: hole.hole_height / 2 + margin,
|
|
510
|
+
fill: positiveMarginColor,
|
|
511
|
+
realToCanvasMat
|
|
512
|
+
});
|
|
513
|
+
}
|
|
482
514
|
drawOval({
|
|
483
515
|
ctx,
|
|
484
516
|
center: { x: hole.x, y: hole.y },
|
|
@@ -490,6 +522,16 @@ function drawPcbHole(params) {
|
|
|
490
522
|
return;
|
|
491
523
|
}
|
|
492
524
|
if (hole.hole_shape === "rect") {
|
|
525
|
+
if (hasSoldermask && margin > 0) {
|
|
526
|
+
drawRect({
|
|
527
|
+
ctx,
|
|
528
|
+
center: { x: hole.x, y: hole.y },
|
|
529
|
+
width: hole.hole_width + margin * 2,
|
|
530
|
+
height: hole.hole_height + margin * 2,
|
|
531
|
+
fill: positiveMarginColor,
|
|
532
|
+
realToCanvasMat
|
|
533
|
+
});
|
|
534
|
+
}
|
|
493
535
|
drawRect({
|
|
494
536
|
ctx,
|
|
495
537
|
center: { x: hole.x, y: hole.y },
|
|
@@ -501,6 +543,16 @@ function drawPcbHole(params) {
|
|
|
501
543
|
return;
|
|
502
544
|
}
|
|
503
545
|
if (hole.hole_shape === "pill") {
|
|
546
|
+
if (hasSoldermask && margin > 0) {
|
|
547
|
+
drawPill({
|
|
548
|
+
ctx,
|
|
549
|
+
center: { x: hole.x, y: hole.y },
|
|
550
|
+
width: hole.hole_width + margin * 2,
|
|
551
|
+
height: hole.hole_height + margin * 2,
|
|
552
|
+
fill: positiveMarginColor,
|
|
553
|
+
realToCanvasMat
|
|
554
|
+
});
|
|
555
|
+
}
|
|
504
556
|
drawPill({
|
|
505
557
|
ctx,
|
|
506
558
|
center: { x: hole.x, y: hole.y },
|
|
@@ -512,6 +564,18 @@ function drawPcbHole(params) {
|
|
|
512
564
|
return;
|
|
513
565
|
}
|
|
514
566
|
if (hole.hole_shape === "rotated_pill") {
|
|
567
|
+
const rotation = hole.ccw_rotation ?? 0;
|
|
568
|
+
if (hasSoldermask && margin > 0) {
|
|
569
|
+
drawPill({
|
|
570
|
+
ctx,
|
|
571
|
+
center: { x: hole.x, y: hole.y },
|
|
572
|
+
width: hole.hole_width + margin * 2,
|
|
573
|
+
height: hole.hole_height + margin * 2,
|
|
574
|
+
fill: positiveMarginColor,
|
|
575
|
+
realToCanvasMat,
|
|
576
|
+
rotation
|
|
577
|
+
});
|
|
578
|
+
}
|
|
515
579
|
drawPill({
|
|
516
580
|
ctx,
|
|
517
581
|
center: { x: hole.x, y: hole.y },
|
|
@@ -519,7 +583,7 @@ function drawPcbHole(params) {
|
|
|
519
583
|
height: hole.hole_height,
|
|
520
584
|
fill: colorMap.drill,
|
|
521
585
|
realToCanvasMat,
|
|
522
|
-
rotation
|
|
586
|
+
rotation
|
|
523
587
|
});
|
|
524
588
|
return;
|
|
525
589
|
}
|
|
@@ -699,6 +763,37 @@ function drawSoldermaskRingForPill(ctx, center, width, height, margin, rotation,
|
|
|
699
763
|
}
|
|
700
764
|
ctx.restore();
|
|
701
765
|
}
|
|
766
|
+
function drawSoldermaskRingForOval(ctx, center, radius_x, radius_y, margin, rotation, realToCanvasMat, soldermaskColor, holeColor) {
|
|
767
|
+
const [cx, cy] = applyToPoint6(realToCanvasMat, [center.x, center.y]);
|
|
768
|
+
const scaledRadiusX = radius_x * Math.abs(realToCanvasMat.a);
|
|
769
|
+
const scaledRadiusY = radius_y * Math.abs(realToCanvasMat.a);
|
|
770
|
+
const scaledMargin = Math.abs(margin) * Math.abs(realToCanvasMat.a);
|
|
771
|
+
ctx.save();
|
|
772
|
+
ctx.translate(cx, cy);
|
|
773
|
+
if (rotation !== 0) {
|
|
774
|
+
ctx.rotate(-rotation * (Math.PI / 180));
|
|
775
|
+
}
|
|
776
|
+
const prevCompositeOp = ctx.globalCompositeOperation;
|
|
777
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
778
|
+
ctx.globalCompositeOperation = "source-atop";
|
|
779
|
+
}
|
|
780
|
+
ctx.beginPath();
|
|
781
|
+
ctx.ellipse(0, 0, scaledRadiusX, scaledRadiusY, 0, 0, Math.PI * 2);
|
|
782
|
+
ctx.fillStyle = soldermaskColor;
|
|
783
|
+
ctx.fill();
|
|
784
|
+
if (ctx.globalCompositeOperation !== void 0) {
|
|
785
|
+
ctx.globalCompositeOperation = prevCompositeOp || "source-over";
|
|
786
|
+
}
|
|
787
|
+
const innerRadiusX = Math.max(0, scaledRadiusX - scaledMargin);
|
|
788
|
+
const innerRadiusY = Math.max(0, scaledRadiusY - scaledMargin);
|
|
789
|
+
if (innerRadiusX > 0 && innerRadiusY > 0) {
|
|
790
|
+
ctx.beginPath();
|
|
791
|
+
ctx.ellipse(0, 0, innerRadiusX, innerRadiusY, 0, 0, Math.PI * 2);
|
|
792
|
+
ctx.fillStyle = holeColor;
|
|
793
|
+
ctx.fill();
|
|
794
|
+
}
|
|
795
|
+
ctx.restore();
|
|
796
|
+
}
|
|
702
797
|
|
|
703
798
|
// lib/drawer/elements/pcb-smtpad.ts
|
|
704
799
|
function layerToColor(layer, colorMap) {
|
|
@@ -1671,78 +1766,76 @@ function drawDimensionLine(params) {
|
|
|
1671
1766
|
x: anchor.x + extensionDirection.x * extensionLength,
|
|
1672
1767
|
y: anchor.y + extensionDirection.y * extensionLength
|
|
1673
1768
|
};
|
|
1674
|
-
const
|
|
1769
|
+
const halfWidth = strokeWidth / 2;
|
|
1675
1770
|
const extPerpendicular = {
|
|
1676
1771
|
x: -extensionDirection.y,
|
|
1677
1772
|
y: extensionDirection.x
|
|
1678
1773
|
};
|
|
1679
1774
|
return [
|
|
1680
1775
|
{
|
|
1681
|
-
x: anchor.x + extPerpendicular.x *
|
|
1682
|
-
y: anchor.y + extPerpendicular.y *
|
|
1776
|
+
x: anchor.x + extPerpendicular.x * halfWidth,
|
|
1777
|
+
y: anchor.y + extPerpendicular.y * halfWidth
|
|
1683
1778
|
},
|
|
1684
1779
|
{
|
|
1685
|
-
x: anchor.x - extPerpendicular.x *
|
|
1686
|
-
y: anchor.y - extPerpendicular.y *
|
|
1780
|
+
x: anchor.x - extPerpendicular.x * halfWidth,
|
|
1781
|
+
y: anchor.y - extPerpendicular.y * halfWidth
|
|
1687
1782
|
},
|
|
1688
1783
|
{
|
|
1689
|
-
x: endPoint.x - extPerpendicular.x *
|
|
1690
|
-
y: endPoint.y - extPerpendicular.y *
|
|
1784
|
+
x: endPoint.x - extPerpendicular.x * halfWidth,
|
|
1785
|
+
y: endPoint.y - extPerpendicular.y * halfWidth
|
|
1691
1786
|
},
|
|
1692
1787
|
{
|
|
1693
|
-
x: endPoint.x + extPerpendicular.x *
|
|
1694
|
-
y: endPoint.y + extPerpendicular.y *
|
|
1788
|
+
x: endPoint.x + extPerpendicular.x * halfWidth,
|
|
1789
|
+
y: endPoint.y + extPerpendicular.y * halfWidth
|
|
1695
1790
|
}
|
|
1696
1791
|
];
|
|
1697
1792
|
};
|
|
1698
|
-
|
|
1699
|
-
allPoints.push(
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
allPoints
|
|
1734
|
-
const
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
];
|
|
1745
|
-
allPoints.push(...arrow2, arrow2[0]);
|
|
1793
|
+
allPoints.push(fromOffset);
|
|
1794
|
+
allPoints.push({
|
|
1795
|
+
x: fromBase.x + perpendicular.x * (arrowSize / 2),
|
|
1796
|
+
y: fromBase.y + perpendicular.y * (arrowSize / 2)
|
|
1797
|
+
});
|
|
1798
|
+
allPoints.push({
|
|
1799
|
+
x: fromBase.x + perpendicular.x * (strokeWidth / 2),
|
|
1800
|
+
y: fromBase.y + perpendicular.y * (strokeWidth / 2)
|
|
1801
|
+
});
|
|
1802
|
+
allPoints.push({
|
|
1803
|
+
x: toBase.x + perpendicular.x * (strokeWidth / 2),
|
|
1804
|
+
y: toBase.y + perpendicular.y * (strokeWidth / 2)
|
|
1805
|
+
});
|
|
1806
|
+
allPoints.push({
|
|
1807
|
+
x: toBase.x + perpendicular.x * (arrowSize / 2),
|
|
1808
|
+
y: toBase.y + perpendicular.y * (arrowSize / 2)
|
|
1809
|
+
});
|
|
1810
|
+
allPoints.push(toOffset);
|
|
1811
|
+
allPoints.push({
|
|
1812
|
+
x: toBase.x - perpendicular.x * (arrowSize / 2),
|
|
1813
|
+
y: toBase.y - perpendicular.y * (arrowSize / 2)
|
|
1814
|
+
});
|
|
1815
|
+
allPoints.push({
|
|
1816
|
+
x: toBase.x - perpendicular.x * (strokeWidth / 2),
|
|
1817
|
+
y: toBase.y - perpendicular.y * (strokeWidth / 2)
|
|
1818
|
+
});
|
|
1819
|
+
allPoints.push({
|
|
1820
|
+
x: fromBase.x - perpendicular.x * (strokeWidth / 2),
|
|
1821
|
+
y: fromBase.y - perpendicular.y * (strokeWidth / 2)
|
|
1822
|
+
});
|
|
1823
|
+
allPoints.push({
|
|
1824
|
+
x: fromBase.x - perpendicular.x * (arrowSize / 2),
|
|
1825
|
+
y: fromBase.y - perpendicular.y * (arrowSize / 2)
|
|
1826
|
+
});
|
|
1827
|
+
allPoints.push(fromOffset);
|
|
1828
|
+
const startPoint = allPoints[0];
|
|
1829
|
+
const addTick = (anchor) => {
|
|
1830
|
+
const pts = getExtensionPoints(anchor);
|
|
1831
|
+
allPoints.push(startPoint);
|
|
1832
|
+
allPoints.push(pts[0]);
|
|
1833
|
+
allPoints.push(...pts);
|
|
1834
|
+
allPoints.push(pts[0]);
|
|
1835
|
+
allPoints.push(startPoint);
|
|
1836
|
+
};
|
|
1837
|
+
addTick(from);
|
|
1838
|
+
addTick(to);
|
|
1746
1839
|
drawPolygon({
|
|
1747
1840
|
ctx,
|
|
1748
1841
|
points: allPoints,
|
|
@@ -1778,9 +1871,9 @@ function drawDimensionLine(params) {
|
|
|
1778
1871
|
const rotationRad = textRotation * Math.PI / 180;
|
|
1779
1872
|
const sinRot = Math.abs(Math.sin(rotationRad));
|
|
1780
1873
|
const cosRot = Math.abs(Math.cos(rotationRad));
|
|
1781
|
-
const
|
|
1874
|
+
const halfWidth = textWidth / 2;
|
|
1782
1875
|
const halfHeight = textHeight / 2;
|
|
1783
|
-
const maxExtension =
|
|
1876
|
+
const maxExtension = halfWidth * sinRot + halfHeight * cosRot;
|
|
1784
1877
|
additionalOffset = maxExtension + fontSize * TEXT_INTERSECTION_PADDING_MULTIPLIER;
|
|
1785
1878
|
}
|
|
1786
1879
|
const textOffset = arrowSize * TEXT_OFFSET_MULTIPLIER + additionalOffset;
|
|
@@ -1941,8 +2034,11 @@ var CircuitToCanvasDrawer = class {
|
|
|
1941
2034
|
const hasSoldermaskPads = elements.some(
|
|
1942
2035
|
(el) => el.type === "pcb_smtpad" && el.is_covered_with_solder_mask === true
|
|
1943
2036
|
);
|
|
2037
|
+
const hasSoldermaskHoles = elements.some(
|
|
2038
|
+
(el) => el.type === "pcb_hole" && el.is_covered_with_solder_mask === true
|
|
2039
|
+
);
|
|
1944
2040
|
for (const element of elements) {
|
|
1945
|
-
if (element.type === "pcb_board" && hasSoldermaskPads) {
|
|
2041
|
+
if (element.type === "pcb_board" && (hasSoldermaskPads || hasSoldermaskHoles)) {
|
|
1946
2042
|
this.drawBoardWithSoldermask(element);
|
|
1947
2043
|
} else {
|
|
1948
2044
|
this.drawElement(element, options);
|
|
@@ -2269,6 +2365,7 @@ export {
|
|
|
2269
2365
|
drawPolygon,
|
|
2270
2366
|
drawRect,
|
|
2271
2367
|
drawSoldermaskRingForCircle,
|
|
2368
|
+
drawSoldermaskRingForOval,
|
|
2272
2369
|
drawSoldermaskRingForPill,
|
|
2273
2370
|
drawSoldermaskRingForRect,
|
|
2274
2371
|
drawText,
|
|
@@ -160,16 +160,25 @@ export class CircuitToCanvasDrawer {
|
|
|
160
160
|
elements: AnyCircuitElement[],
|
|
161
161
|
options: DrawElementsOptions = {},
|
|
162
162
|
): void {
|
|
163
|
-
// Check if any pad has is_covered_with_solder_mask: true
|
|
163
|
+
// Check if any pad or hole has is_covered_with_solder_mask: true
|
|
164
164
|
const hasSoldermaskPads = elements.some(
|
|
165
165
|
(el) =>
|
|
166
166
|
el.type === "pcb_smtpad" &&
|
|
167
167
|
(el as PcbSmtPad).is_covered_with_solder_mask === true,
|
|
168
168
|
)
|
|
169
|
+
const hasSoldermaskHoles = elements.some(
|
|
170
|
+
(el) =>
|
|
171
|
+
el.type === "pcb_hole" &&
|
|
172
|
+
(el as PcbHole & { is_covered_with_solder_mask?: boolean })
|
|
173
|
+
.is_covered_with_solder_mask === true,
|
|
174
|
+
)
|
|
169
175
|
|
|
170
176
|
for (const element of elements) {
|
|
171
|
-
if (
|
|
172
|
-
|
|
177
|
+
if (
|
|
178
|
+
element.type === "pcb_board" &&
|
|
179
|
+
(hasSoldermaskPads || hasSoldermaskHoles)
|
|
180
|
+
) {
|
|
181
|
+
// Draw board with soldermask fill when pads or holes have soldermask
|
|
173
182
|
this.drawBoardWithSoldermask(element as PcbBoard)
|
|
174
183
|
} else {
|
|
175
184
|
this.drawElement(element, options)
|
|
@@ -16,7 +16,26 @@ export interface DrawPcbHoleParams {
|
|
|
16
16
|
export function drawPcbHole(params: DrawPcbHoleParams): void {
|
|
17
17
|
const { ctx, hole, realToCanvasMat, colorMap } = params
|
|
18
18
|
|
|
19
|
+
const hasSoldermask =
|
|
20
|
+
hole.is_covered_with_solder_mask === true &&
|
|
21
|
+
hole.soldermask_margin !== undefined &&
|
|
22
|
+
hole.soldermask_margin > 0
|
|
23
|
+
const margin = hasSoldermask ? hole.soldermask_margin! : 0
|
|
24
|
+
const positiveMarginColor = colorMap.substrate
|
|
25
|
+
|
|
19
26
|
if (hole.hole_shape === "circle") {
|
|
27
|
+
// For positive margins, draw extended mask area first
|
|
28
|
+
if (hasSoldermask && margin > 0) {
|
|
29
|
+
drawCircle({
|
|
30
|
+
ctx,
|
|
31
|
+
center: { x: hole.x, y: hole.y },
|
|
32
|
+
radius: hole.hole_diameter / 2 + margin,
|
|
33
|
+
fill: positiveMarginColor,
|
|
34
|
+
realToCanvasMat,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Draw the hole
|
|
20
39
|
drawCircle({
|
|
21
40
|
ctx,
|
|
22
41
|
center: { x: hole.x, y: hole.y },
|
|
@@ -28,6 +47,19 @@ export function drawPcbHole(params: DrawPcbHoleParams): void {
|
|
|
28
47
|
}
|
|
29
48
|
|
|
30
49
|
if (hole.hole_shape === "square") {
|
|
50
|
+
// For positive margins, draw extended mask area first
|
|
51
|
+
if (hasSoldermask && margin > 0) {
|
|
52
|
+
drawRect({
|
|
53
|
+
ctx,
|
|
54
|
+
center: { x: hole.x, y: hole.y },
|
|
55
|
+
width: hole.hole_diameter + margin * 2,
|
|
56
|
+
height: hole.hole_diameter + margin * 2,
|
|
57
|
+
fill: positiveMarginColor,
|
|
58
|
+
realToCanvasMat,
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Draw the hole
|
|
31
63
|
drawRect({
|
|
32
64
|
ctx,
|
|
33
65
|
center: { x: hole.x, y: hole.y },
|
|
@@ -40,6 +72,19 @@ export function drawPcbHole(params: DrawPcbHoleParams): void {
|
|
|
40
72
|
}
|
|
41
73
|
|
|
42
74
|
if (hole.hole_shape === "oval") {
|
|
75
|
+
// For positive margins, draw extended mask area first
|
|
76
|
+
if (hasSoldermask && margin > 0) {
|
|
77
|
+
drawOval({
|
|
78
|
+
ctx,
|
|
79
|
+
center: { x: hole.x, y: hole.y },
|
|
80
|
+
radius_x: hole.hole_width / 2 + margin,
|
|
81
|
+
radius_y: hole.hole_height / 2 + margin,
|
|
82
|
+
fill: positiveMarginColor,
|
|
83
|
+
realToCanvasMat,
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Draw the hole
|
|
43
88
|
drawOval({
|
|
44
89
|
ctx,
|
|
45
90
|
center: { x: hole.x, y: hole.y },
|
|
@@ -52,6 +97,19 @@ export function drawPcbHole(params: DrawPcbHoleParams): void {
|
|
|
52
97
|
}
|
|
53
98
|
|
|
54
99
|
if (hole.hole_shape === "rect") {
|
|
100
|
+
// For positive margins, draw extended mask area first
|
|
101
|
+
if (hasSoldermask && margin > 0) {
|
|
102
|
+
drawRect({
|
|
103
|
+
ctx,
|
|
104
|
+
center: { x: hole.x, y: hole.y },
|
|
105
|
+
width: hole.hole_width + margin * 2,
|
|
106
|
+
height: hole.hole_height + margin * 2,
|
|
107
|
+
fill: positiveMarginColor,
|
|
108
|
+
realToCanvasMat,
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Draw the hole
|
|
55
113
|
drawRect({
|
|
56
114
|
ctx,
|
|
57
115
|
center: { x: hole.x, y: hole.y },
|
|
@@ -64,6 +122,19 @@ export function drawPcbHole(params: DrawPcbHoleParams): void {
|
|
|
64
122
|
}
|
|
65
123
|
|
|
66
124
|
if (hole.hole_shape === "pill") {
|
|
125
|
+
// For positive margins, draw extended mask area first
|
|
126
|
+
if (hasSoldermask && margin > 0) {
|
|
127
|
+
drawPill({
|
|
128
|
+
ctx,
|
|
129
|
+
center: { x: hole.x, y: hole.y },
|
|
130
|
+
width: hole.hole_width + margin * 2,
|
|
131
|
+
height: hole.hole_height + margin * 2,
|
|
132
|
+
fill: positiveMarginColor,
|
|
133
|
+
realToCanvasMat,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Draw the hole
|
|
67
138
|
drawPill({
|
|
68
139
|
ctx,
|
|
69
140
|
center: { x: hole.x, y: hole.y },
|
|
@@ -76,6 +147,22 @@ export function drawPcbHole(params: DrawPcbHoleParams): void {
|
|
|
76
147
|
}
|
|
77
148
|
|
|
78
149
|
if (hole.hole_shape === "rotated_pill") {
|
|
150
|
+
const rotation = (hole as any).ccw_rotation ?? 0
|
|
151
|
+
|
|
152
|
+
// For positive margins, draw extended mask area first
|
|
153
|
+
if (hasSoldermask && margin > 0) {
|
|
154
|
+
drawPill({
|
|
155
|
+
ctx,
|
|
156
|
+
center: { x: hole.x, y: hole.y },
|
|
157
|
+
width: hole.hole_width + margin * 2,
|
|
158
|
+
height: hole.hole_height + margin * 2,
|
|
159
|
+
fill: positiveMarginColor,
|
|
160
|
+
realToCanvasMat,
|
|
161
|
+
rotation,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Draw the hole
|
|
79
166
|
drawPill({
|
|
80
167
|
ctx,
|
|
81
168
|
center: { x: hole.x, y: hole.y },
|
|
@@ -83,7 +170,7 @@ export function drawPcbHole(params: DrawPcbHoleParams): void {
|
|
|
83
170
|
height: hole.hole_height,
|
|
84
171
|
fill: colorMap.drill,
|
|
85
172
|
realToCanvasMat,
|
|
86
|
-
rotation
|
|
173
|
+
rotation,
|
|
87
174
|
})
|
|
88
175
|
return
|
|
89
176
|
}
|
|
@@ -264,3 +264,63 @@ export function drawSoldermaskRingForPill(
|
|
|
264
264
|
|
|
265
265
|
ctx.restore()
|
|
266
266
|
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Draws a soldermask ring for oval shapes with negative margin
|
|
270
|
+
* (soldermask appears inside the hole boundary)
|
|
271
|
+
*/
|
|
272
|
+
export function drawSoldermaskRingForOval(
|
|
273
|
+
ctx: CanvasContext,
|
|
274
|
+
center: { x: number; y: number },
|
|
275
|
+
radius_x: number,
|
|
276
|
+
radius_y: number,
|
|
277
|
+
margin: number,
|
|
278
|
+
rotation: number,
|
|
279
|
+
realToCanvasMat: Matrix,
|
|
280
|
+
soldermaskColor: string,
|
|
281
|
+
holeColor: string,
|
|
282
|
+
): void {
|
|
283
|
+
const [cx, cy] = applyToPoint(realToCanvasMat, [center.x, center.y])
|
|
284
|
+
const scaledRadiusX = radius_x * Math.abs(realToCanvasMat.a)
|
|
285
|
+
const scaledRadiusY = radius_y * Math.abs(realToCanvasMat.a)
|
|
286
|
+
const scaledMargin = Math.abs(margin) * Math.abs(realToCanvasMat.a)
|
|
287
|
+
|
|
288
|
+
ctx.save()
|
|
289
|
+
ctx.translate(cx, cy)
|
|
290
|
+
|
|
291
|
+
if (rotation !== 0) {
|
|
292
|
+
ctx.rotate(-rotation * (Math.PI / 180))
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// For negative margins, outer is hole boundary, inner is reduced by margin
|
|
296
|
+
// Use source-atop so the ring only appears on the hole
|
|
297
|
+
const prevCompositeOp = ctx.globalCompositeOperation
|
|
298
|
+
if (ctx.globalCompositeOperation !== undefined) {
|
|
299
|
+
ctx.globalCompositeOperation = "source-atop"
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Draw outer oval filled (at hole boundary)
|
|
303
|
+
ctx.beginPath()
|
|
304
|
+
ctx.ellipse(0, 0, scaledRadiusX, scaledRadiusY, 0, 0, Math.PI * 2)
|
|
305
|
+
ctx.fillStyle = soldermaskColor
|
|
306
|
+
ctx.fill()
|
|
307
|
+
|
|
308
|
+
// Reset composite operation and restore hole color in inner area
|
|
309
|
+
if (ctx.globalCompositeOperation !== undefined) {
|
|
310
|
+
ctx.globalCompositeOperation = prevCompositeOp || "source-over"
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Restore hole color in inner oval (reduced by margin)
|
|
314
|
+
// For ovals, we reduce both radii by the margin
|
|
315
|
+
const innerRadiusX = Math.max(0, scaledRadiusX - scaledMargin)
|
|
316
|
+
const innerRadiusY = Math.max(0, scaledRadiusY - scaledMargin)
|
|
317
|
+
|
|
318
|
+
if (innerRadiusX > 0 && innerRadiusY > 0) {
|
|
319
|
+
ctx.beginPath()
|
|
320
|
+
ctx.ellipse(0, 0, innerRadiusX, innerRadiusY, 0, 0, Math.PI * 2)
|
|
321
|
+
ctx.fillStyle = holeColor
|
|
322
|
+
ctx.fill()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
ctx.restore()
|
|
326
|
+
}
|
|
@@ -121,85 +121,80 @@ export function drawDimensionLine(params: DrawDimensionLineParams): void {
|
|
|
121
121
|
]
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
allPoints.push(
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
allPoints.push(
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
direction.y * arrowSize -
|
|
199
|
-
perpendicular.y * (arrowSize / 2),
|
|
200
|
-
},
|
|
201
|
-
]
|
|
202
|
-
allPoints.push(...arrow2, arrow2[0]!)
|
|
124
|
+
// Unified Perimeter Approach:
|
|
125
|
+
// Draw the main line and both arrows as a single continuous perimeter path.
|
|
126
|
+
// This eliminates self-intersections and winding issues.
|
|
127
|
+
|
|
128
|
+
// 1. Tip 1
|
|
129
|
+
allPoints.push(fromOffset)
|
|
130
|
+
|
|
131
|
+
// 2. Arrow 1 base corner 1
|
|
132
|
+
allPoints.push({
|
|
133
|
+
x: fromBase.x + perpendicular.x * (arrowSize / 2),
|
|
134
|
+
y: fromBase.y + perpendicular.y * (arrowSize / 2),
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// 3. Main Line corner 1
|
|
138
|
+
allPoints.push({
|
|
139
|
+
x: fromBase.x + perpendicular.x * (strokeWidth / 2),
|
|
140
|
+
y: fromBase.y + perpendicular.y * (strokeWidth / 2),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// 4. Main Line corner 2 (at toBase)
|
|
144
|
+
allPoints.push({
|
|
145
|
+
x: toBase.x + perpendicular.x * (strokeWidth / 2),
|
|
146
|
+
y: toBase.y + perpendicular.y * (strokeWidth / 2),
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// 5. Arrow 2 base corner 1
|
|
150
|
+
allPoints.push({
|
|
151
|
+
x: toBase.x + perpendicular.x * (arrowSize / 2),
|
|
152
|
+
y: toBase.y + perpendicular.y * (arrowSize / 2),
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// 6. Tip 2
|
|
156
|
+
allPoints.push(toOffset)
|
|
157
|
+
|
|
158
|
+
// 7. Arrow 2 base corner 2
|
|
159
|
+
allPoints.push({
|
|
160
|
+
x: toBase.x - perpendicular.x * (arrowSize / 2),
|
|
161
|
+
y: toBase.y - perpendicular.y * (arrowSize / 2),
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// 8. Main Line corner 3 (at toBase)
|
|
165
|
+
allPoints.push({
|
|
166
|
+
x: toBase.x - perpendicular.x * (strokeWidth / 2),
|
|
167
|
+
y: toBase.y - perpendicular.y * (strokeWidth / 2),
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// 9. Main Line corner 4 (at fromBase)
|
|
171
|
+
allPoints.push({
|
|
172
|
+
x: fromBase.x - perpendicular.x * (strokeWidth / 2),
|
|
173
|
+
y: fromBase.y - perpendicular.y * (strokeWidth / 2),
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
// 10. Arrow 1 base corner 2
|
|
177
|
+
allPoints.push({
|
|
178
|
+
x: fromBase.x - perpendicular.x * (arrowSize / 2),
|
|
179
|
+
y: fromBase.y - perpendicular.y * (arrowSize / 2),
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// 11. Back to Tip 1
|
|
183
|
+
allPoints.push(fromOffset)
|
|
184
|
+
|
|
185
|
+
// Bridge Ticks (Extension lines)
|
|
186
|
+
const startPoint = allPoints[0]!
|
|
187
|
+
const addTick = (anchor: { x: number; y: number }) => {
|
|
188
|
+
const pts = getExtensionPoints(anchor)
|
|
189
|
+
allPoints.push(startPoint)
|
|
190
|
+
allPoints.push(pts[0]!)
|
|
191
|
+
allPoints.push(...pts)
|
|
192
|
+
allPoints.push(pts[0]!)
|
|
193
|
+
allPoints.push(startPoint)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
addTick(from)
|
|
197
|
+
addTick(to)
|
|
203
198
|
|
|
204
199
|
drawPolygon({
|
|
205
200
|
ctx,
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
+
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
4
|
+
|
|
5
|
+
test("draw holes with positive soldermask margins", async () => {
|
|
6
|
+
const canvas = createCanvas(800, 600)
|
|
7
|
+
const ctx = canvas.getContext("2d")
|
|
8
|
+
const drawer = new CircuitToCanvasDrawer(ctx)
|
|
9
|
+
|
|
10
|
+
ctx.fillStyle = "#1a1a1a"
|
|
11
|
+
ctx.fillRect(0, 0, 800, 600)
|
|
12
|
+
|
|
13
|
+
const circuit: any = [
|
|
14
|
+
{
|
|
15
|
+
type: "pcb_board",
|
|
16
|
+
pcb_board_id: "board0",
|
|
17
|
+
center: { x: 0, y: 0 },
|
|
18
|
+
width: 14,
|
|
19
|
+
height: 10,
|
|
20
|
+
},
|
|
21
|
+
// Circle with positive margin (mask extends beyond hole)
|
|
22
|
+
{
|
|
23
|
+
type: "pcb_hole",
|
|
24
|
+
pcb_hole_id: "hole_circle_positive",
|
|
25
|
+
hole_shape: "circle",
|
|
26
|
+
x: -4,
|
|
27
|
+
y: 2,
|
|
28
|
+
hole_diameter: 1.0,
|
|
29
|
+
is_covered_with_solder_mask: true,
|
|
30
|
+
soldermask_margin: 0.2,
|
|
31
|
+
},
|
|
32
|
+
// Square with positive margin
|
|
33
|
+
{
|
|
34
|
+
type: "pcb_hole",
|
|
35
|
+
pcb_hole_id: "hole_square_positive",
|
|
36
|
+
hole_shape: "square",
|
|
37
|
+
x: -1,
|
|
38
|
+
y: 2,
|
|
39
|
+
hole_diameter: 1.0,
|
|
40
|
+
is_covered_with_solder_mask: true,
|
|
41
|
+
soldermask_margin: 0.15,
|
|
42
|
+
},
|
|
43
|
+
// Oval with positive margin
|
|
44
|
+
{
|
|
45
|
+
type: "pcb_hole",
|
|
46
|
+
pcb_hole_id: "hole_oval_positive",
|
|
47
|
+
hole_shape: "oval",
|
|
48
|
+
x: 2,
|
|
49
|
+
y: 2,
|
|
50
|
+
hole_width: 1.5,
|
|
51
|
+
hole_height: 0.8,
|
|
52
|
+
is_covered_with_solder_mask: true,
|
|
53
|
+
soldermask_margin: 0.1,
|
|
54
|
+
},
|
|
55
|
+
// Rect with positive margin
|
|
56
|
+
{
|
|
57
|
+
type: "pcb_hole",
|
|
58
|
+
pcb_hole_id: "hole_rect_positive",
|
|
59
|
+
hole_shape: "rect",
|
|
60
|
+
x: 5,
|
|
61
|
+
y: 2,
|
|
62
|
+
hole_width: 1.6,
|
|
63
|
+
hole_height: 1.1,
|
|
64
|
+
is_covered_with_solder_mask: true,
|
|
65
|
+
soldermask_margin: 0.15,
|
|
66
|
+
},
|
|
67
|
+
// Pill with positive margin
|
|
68
|
+
{
|
|
69
|
+
type: "pcb_hole",
|
|
70
|
+
pcb_hole_id: "hole_pill_positive",
|
|
71
|
+
hole_shape: "pill",
|
|
72
|
+
x: -2.5,
|
|
73
|
+
y: 0,
|
|
74
|
+
hole_width: 2.0,
|
|
75
|
+
hole_height: 0.8,
|
|
76
|
+
is_covered_with_solder_mask: true,
|
|
77
|
+
soldermask_margin: 0.1,
|
|
78
|
+
},
|
|
79
|
+
// Silkscreen labels for positive margin holes
|
|
80
|
+
{
|
|
81
|
+
type: "pcb_silkscreen_text",
|
|
82
|
+
pcb_silkscreen_text_id: "text_circle_pos",
|
|
83
|
+
layer: "top",
|
|
84
|
+
anchor_position: { x: -4, y: 3.2 },
|
|
85
|
+
anchor_alignment: "center",
|
|
86
|
+
text: "+0.2mm",
|
|
87
|
+
font_size: 0.4,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
type: "pcb_silkscreen_text",
|
|
91
|
+
pcb_silkscreen_text_id: "text_square_pos",
|
|
92
|
+
layer: "top",
|
|
93
|
+
anchor_position: { x: -1, y: 3.2 },
|
|
94
|
+
anchor_alignment: "center",
|
|
95
|
+
text: "+0.15mm",
|
|
96
|
+
font_size: 0.4,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: "pcb_silkscreen_text",
|
|
100
|
+
pcb_silkscreen_text_id: "text_oval_pos",
|
|
101
|
+
layer: "top",
|
|
102
|
+
anchor_position: { x: 2, y: 3.2 },
|
|
103
|
+
anchor_alignment: "center",
|
|
104
|
+
text: "+0.1mm",
|
|
105
|
+
font_size: 0.4,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type: "pcb_silkscreen_text",
|
|
109
|
+
pcb_silkscreen_text_id: "text_rect_pos",
|
|
110
|
+
layer: "top",
|
|
111
|
+
anchor_position: { x: 5, y: 3.2 },
|
|
112
|
+
anchor_alignment: "center",
|
|
113
|
+
text: "+0.15mm",
|
|
114
|
+
font_size: 0.4,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: "pcb_silkscreen_text",
|
|
118
|
+
pcb_silkscreen_text_id: "text_pill_pos",
|
|
119
|
+
layer: "top",
|
|
120
|
+
anchor_position: { x: -2.5, y: 1 },
|
|
121
|
+
anchor_alignment: "center",
|
|
122
|
+
text: "+0.1mm",
|
|
123
|
+
font_size: 0.4,
|
|
124
|
+
},
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
drawer.setCameraBounds({ minX: -7, maxX: 7, minY: -5, maxY: 5 })
|
|
128
|
+
drawer.drawElements(circuit)
|
|
129
|
+
|
|
130
|
+
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
131
|
+
import.meta.path,
|
|
132
|
+
)
|
|
133
|
+
})
|