circuit-to-canvas 0.0.41 → 0.0.42
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 +12 -2
- package/dist/index.js +116 -19
- package/lib/drawer/CircuitToCanvasDrawer.ts +11 -0
- package/lib/drawer/elements/index.ts +2 -0
- package/lib/drawer/elements/pcb-keepout.ts +136 -0
- package/lib/drawer/shapes/rect.ts +6 -7
- package/lib/drawer/types.ts +3 -0
- package/package.json +1 -1
- package/tests/elements/__snapshots__/multi-segment-trace.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-rect-all-features.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-fabrication-note-rect-dashed.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-keepout-layer-filter.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-keepout-multiple-layers.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-keepout-rect-and-circle.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-keepout-with-group-id.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-note-rect-all-features.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-note-rect-dashed-stroke.snap.png +0 -0
- package/tests/elements/__snapshots__/pcb-smtpad-soldermask-coverage.snap.png +0 -0
- package/tests/elements/__snapshots__/with-group-id.snap.png +0 -0
- package/tests/elements/pcb-keepout-layer-filter.test.ts +51 -0
- package/tests/elements/pcb-keepout-multiple-layers.test.ts +51 -0
- package/tests/elements/pcb-keepout-rect-and-circle.test.ts +60 -0
- package/tests/elements/pcb-keepout-with-group-id.test.ts +45 -0
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyCircuitElement, PcbRenderLayer, NinePointAnchor, PcbPlatedHole, PCBVia, PCBHole, PcbSmtPad, PCBTrace, PcbBoard, PcbSilkscreenText, PcbSilkscreenRect, PcbSilkscreenCircle, PcbSilkscreenLine, PcbSilkscreenPath, PcbSilkscreenPill, PcbSilkscreenOval, PcbCutout, PcbCopperPour, PcbCopperText, PcbFabricationNoteText, PcbFabricationNoteRect, PcbNoteRect, PcbFabricationNotePath, PcbNotePath, PcbNoteText, PcbNoteDimension, PcbFabricationNoteDimension } from 'circuit-json';
|
|
1
|
+
import { AnyCircuitElement, PcbRenderLayer, NinePointAnchor, PcbPlatedHole, PCBVia, PCBHole, PcbSmtPad, PCBTrace, PcbBoard, PcbSilkscreenText, PcbSilkscreenRect, PcbSilkscreenCircle, PcbSilkscreenLine, PcbSilkscreenPath, PcbSilkscreenPill, PcbSilkscreenOval, PcbCutout, PCBKeepout, PcbCopperPour, PcbCopperText, PcbFabricationNoteText, PcbFabricationNoteRect, PcbNoteRect, PcbFabricationNotePath, PcbNotePath, PcbNoteText, PcbNoteDimension, PcbFabricationNoteDimension } from 'circuit-json';
|
|
2
2
|
import { Matrix } from 'transformation-matrix';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -18,6 +18,7 @@ interface CanvasContext {
|
|
|
18
18
|
moveTo(x: number, y: number): void;
|
|
19
19
|
save(): void;
|
|
20
20
|
restore(): void;
|
|
21
|
+
clip(): void;
|
|
21
22
|
translate(x: number, y: number): void;
|
|
22
23
|
rotate(angle: number): void;
|
|
23
24
|
scale(x: number, y: number): void;
|
|
@@ -68,6 +69,7 @@ interface PcbColorMap {
|
|
|
68
69
|
};
|
|
69
70
|
substrate: string;
|
|
70
71
|
courtyard: string;
|
|
72
|
+
keepout: string;
|
|
71
73
|
}
|
|
72
74
|
declare const DEFAULT_PCB_COLOR_MAP: PcbColorMap;
|
|
73
75
|
interface DrawerConfig {
|
|
@@ -430,6 +432,14 @@ interface DrawPcbCutoutParams {
|
|
|
430
432
|
}
|
|
431
433
|
declare function drawPcbCutout(params: DrawPcbCutoutParams): void;
|
|
432
434
|
|
|
435
|
+
interface DrawPcbKeepoutParams {
|
|
436
|
+
ctx: CanvasContext;
|
|
437
|
+
keepout: PCBKeepout;
|
|
438
|
+
realToCanvasMat: Matrix;
|
|
439
|
+
colorMap: PcbColorMap;
|
|
440
|
+
}
|
|
441
|
+
declare function drawPcbKeepout(params: DrawPcbKeepoutParams): void;
|
|
442
|
+
|
|
433
443
|
interface DrawPcbCopperPourParams {
|
|
434
444
|
ctx: CanvasContext;
|
|
435
445
|
pour: PcbCopperPour;
|
|
@@ -510,4 +520,4 @@ interface DrawPcbFabricationNoteDimensionParams {
|
|
|
510
520
|
}
|
|
511
521
|
declare function drawPcbFabricationNoteDimension(params: DrawPcbFabricationNoteDimensionParams): void;
|
|
512
522
|
|
|
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 };
|
|
523
|
+
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 DrawPcbKeepoutParams, 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, drawPcbKeepout, 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
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
compose,
|
|
5
5
|
translate,
|
|
6
6
|
scale,
|
|
7
|
-
applyToPoint as
|
|
7
|
+
applyToPoint as applyToPoint16
|
|
8
8
|
} from "transformation-matrix";
|
|
9
9
|
|
|
10
10
|
// lib/drawer/pcb-render-layer-filter.ts
|
|
@@ -51,7 +51,9 @@ var DEFAULT_PCB_COLOR_MAP = {
|
|
|
51
51
|
bottom: "#5da9e9"
|
|
52
52
|
},
|
|
53
53
|
boardOutline: "rgba(255, 255, 255, 0.5)",
|
|
54
|
-
courtyard: "#FF00FF"
|
|
54
|
+
courtyard: "#FF00FF",
|
|
55
|
+
keepout: "#FF6B6B"
|
|
56
|
+
// Red color for keepout zones
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
// lib/drawer/shapes/circle.ts
|
|
@@ -92,11 +94,6 @@ function drawRect(params) {
|
|
|
92
94
|
if (rotation !== 0) {
|
|
93
95
|
ctx.rotate(-rotation * (Math.PI / 180));
|
|
94
96
|
}
|
|
95
|
-
if (isStrokeDashed && scaledStrokeWidth) {
|
|
96
|
-
ctx.setLineDash([scaledStrokeWidth * 2, scaledStrokeWidth * 2]);
|
|
97
|
-
} else {
|
|
98
|
-
ctx.setLineDash([]);
|
|
99
|
-
}
|
|
100
97
|
ctx.beginPath();
|
|
101
98
|
if (scaledRadius > 0) {
|
|
102
99
|
const x = -scaledWidth / 2;
|
|
@@ -125,6 +122,11 @@ function drawRect(params) {
|
|
|
125
122
|
ctx.fill();
|
|
126
123
|
}
|
|
127
124
|
if (stroke && scaledStrokeWidth) {
|
|
125
|
+
if (isStrokeDashed) {
|
|
126
|
+
ctx.setLineDash([scaledStrokeWidth * 3, scaledStrokeWidth * 2]);
|
|
127
|
+
} else {
|
|
128
|
+
ctx.setLineDash([]);
|
|
129
|
+
}
|
|
128
130
|
ctx.strokeStyle = stroke;
|
|
129
131
|
ctx.lineWidth = scaledStrokeWidth;
|
|
130
132
|
ctx.stroke();
|
|
@@ -1673,8 +1675,94 @@ function drawPcbCutout(params) {
|
|
|
1673
1675
|
}
|
|
1674
1676
|
}
|
|
1675
1677
|
|
|
1676
|
-
// lib/drawer/elements/pcb-
|
|
1678
|
+
// lib/drawer/elements/pcb-keepout.ts
|
|
1677
1679
|
import { applyToPoint as applyToPoint11 } from "transformation-matrix";
|
|
1680
|
+
function hexToRgba(hex, alpha) {
|
|
1681
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
1682
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
1683
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
1684
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
1685
|
+
}
|
|
1686
|
+
function drawPcbKeepout(params) {
|
|
1687
|
+
const { ctx, keepout, realToCanvasMat, colorMap } = params;
|
|
1688
|
+
const strokeColor = colorMap.keepout;
|
|
1689
|
+
const fillColor = hexToRgba(colorMap.keepout, 0.2);
|
|
1690
|
+
const hatchSpacing = 1;
|
|
1691
|
+
if (keepout.shape === "rect") {
|
|
1692
|
+
const [cx, cy] = applyToPoint11(realToCanvasMat, [
|
|
1693
|
+
keepout.center.x,
|
|
1694
|
+
keepout.center.y
|
|
1695
|
+
]);
|
|
1696
|
+
const scaledWidth = keepout.width * Math.abs(realToCanvasMat.a);
|
|
1697
|
+
const scaledHeight = keepout.height * Math.abs(realToCanvasMat.a);
|
|
1698
|
+
const rotation = keepout.rotation ?? 0;
|
|
1699
|
+
ctx.save();
|
|
1700
|
+
ctx.translate(cx, cy);
|
|
1701
|
+
if (rotation !== 0) {
|
|
1702
|
+
ctx.rotate(-rotation * (Math.PI / 180));
|
|
1703
|
+
}
|
|
1704
|
+
ctx.beginPath();
|
|
1705
|
+
ctx.rect(-scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight);
|
|
1706
|
+
ctx.fillStyle = fillColor;
|
|
1707
|
+
ctx.fill();
|
|
1708
|
+
ctx.beginPath();
|
|
1709
|
+
ctx.rect(-scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight);
|
|
1710
|
+
ctx.clip();
|
|
1711
|
+
const scaledSpacing = hatchSpacing * Math.abs(realToCanvasMat.a);
|
|
1712
|
+
const diagonal = Math.sqrt(
|
|
1713
|
+
scaledWidth * scaledWidth + scaledHeight * scaledHeight
|
|
1714
|
+
);
|
|
1715
|
+
const halfWidth = scaledWidth / 2;
|
|
1716
|
+
const halfHeight = scaledHeight / 2;
|
|
1717
|
+
ctx.strokeStyle = strokeColor;
|
|
1718
|
+
ctx.lineWidth = 0.15 * Math.abs(realToCanvasMat.a);
|
|
1719
|
+
ctx.setLineDash([]);
|
|
1720
|
+
for (let offset = -diagonal; offset < diagonal * 2; offset += scaledSpacing) {
|
|
1721
|
+
ctx.beginPath();
|
|
1722
|
+
const startX = -halfWidth + offset;
|
|
1723
|
+
const startY = -halfHeight;
|
|
1724
|
+
const endX = -halfWidth + offset + diagonal;
|
|
1725
|
+
const endY = -halfHeight + diagonal;
|
|
1726
|
+
ctx.moveTo(startX, startY);
|
|
1727
|
+
ctx.lineTo(endX, endY);
|
|
1728
|
+
ctx.stroke();
|
|
1729
|
+
}
|
|
1730
|
+
ctx.restore();
|
|
1731
|
+
return;
|
|
1732
|
+
}
|
|
1733
|
+
if (keepout.shape === "circle") {
|
|
1734
|
+
const [cx, cy] = applyToPoint11(realToCanvasMat, [
|
|
1735
|
+
keepout.center.x,
|
|
1736
|
+
keepout.center.y
|
|
1737
|
+
]);
|
|
1738
|
+
const scaledRadius = keepout.radius * Math.abs(realToCanvasMat.a);
|
|
1739
|
+
const scaledSpacing = hatchSpacing * Math.abs(realToCanvasMat.a);
|
|
1740
|
+
ctx.save();
|
|
1741
|
+
ctx.translate(cx, cy);
|
|
1742
|
+
ctx.beginPath();
|
|
1743
|
+
ctx.arc(0, 0, scaledRadius, 0, Math.PI * 2);
|
|
1744
|
+
ctx.fillStyle = fillColor;
|
|
1745
|
+
ctx.fill();
|
|
1746
|
+
ctx.beginPath();
|
|
1747
|
+
ctx.arc(0, 0, scaledRadius, 0, Math.PI * 2);
|
|
1748
|
+
ctx.clip();
|
|
1749
|
+
const diagonal = scaledRadius * 2;
|
|
1750
|
+
ctx.strokeStyle = strokeColor;
|
|
1751
|
+
ctx.lineWidth = 0.15 * Math.abs(realToCanvasMat.a);
|
|
1752
|
+
ctx.setLineDash([]);
|
|
1753
|
+
for (let offset = -diagonal; offset < diagonal * 2; offset += scaledSpacing) {
|
|
1754
|
+
ctx.beginPath();
|
|
1755
|
+
ctx.moveTo(offset - diagonal, -diagonal);
|
|
1756
|
+
ctx.lineTo(offset + diagonal, diagonal);
|
|
1757
|
+
ctx.stroke();
|
|
1758
|
+
}
|
|
1759
|
+
ctx.restore();
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
// lib/drawer/elements/pcb-copper-pour.ts
|
|
1765
|
+
import { applyToPoint as applyToPoint12 } from "transformation-matrix";
|
|
1678
1766
|
function layerToColor3(layer, colorMap) {
|
|
1679
1767
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
1680
1768
|
}
|
|
@@ -1683,7 +1771,7 @@ function drawPcbCopperPour(params) {
|
|
|
1683
1771
|
const color = layerToColor3(pour.layer, colorMap);
|
|
1684
1772
|
ctx.save();
|
|
1685
1773
|
if (pour.shape === "rect") {
|
|
1686
|
-
const [cx, cy] =
|
|
1774
|
+
const [cx, cy] = applyToPoint12(realToCanvasMat, [
|
|
1687
1775
|
pour.center.x,
|
|
1688
1776
|
pour.center.y
|
|
1689
1777
|
]);
|
|
@@ -1704,7 +1792,7 @@ function drawPcbCopperPour(params) {
|
|
|
1704
1792
|
if (pour.shape === "polygon") {
|
|
1705
1793
|
if (pour.points && pour.points.length >= 3) {
|
|
1706
1794
|
const canvasPoints = pour.points.map(
|
|
1707
|
-
(p) =>
|
|
1795
|
+
(p) => applyToPoint12(realToCanvasMat, [p.x, p.y])
|
|
1708
1796
|
);
|
|
1709
1797
|
const firstPoint = canvasPoints[0];
|
|
1710
1798
|
if (!firstPoint) {
|
|
@@ -1732,7 +1820,7 @@ function drawPcbCopperPour(params) {
|
|
|
1732
1820
|
}
|
|
1733
1821
|
|
|
1734
1822
|
// lib/drawer/elements/pcb-copper-text.ts
|
|
1735
|
-
import { applyToPoint as
|
|
1823
|
+
import { applyToPoint as applyToPoint13 } from "transformation-matrix";
|
|
1736
1824
|
var DEFAULT_PADDING = { left: 0.2, right: 0.2, top: 0.2, bottom: 0.2 };
|
|
1737
1825
|
function layerToCopperColor(layer, colorMap) {
|
|
1738
1826
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
@@ -1747,7 +1835,7 @@ function drawPcbCopperText(params) {
|
|
|
1747
1835
|
const { ctx, text, realToCanvasMat, colorMap } = params;
|
|
1748
1836
|
const content = text.text ?? "";
|
|
1749
1837
|
if (!content) return;
|
|
1750
|
-
const [x, y] =
|
|
1838
|
+
const [x, y] = applyToPoint13(realToCanvasMat, [
|
|
1751
1839
|
text.anchor_position.x,
|
|
1752
1840
|
text.anchor_position.y
|
|
1753
1841
|
]);
|
|
@@ -1931,7 +2019,7 @@ function drawPcbNoteText(params) {
|
|
|
1931
2019
|
}
|
|
1932
2020
|
|
|
1933
2021
|
// lib/drawer/shapes/dimension-line.ts
|
|
1934
|
-
import { applyToPoint as
|
|
2022
|
+
import { applyToPoint as applyToPoint14 } from "transformation-matrix";
|
|
1935
2023
|
var TEXT_OFFSET_MULTIPLIER = 1.5;
|
|
1936
2024
|
var CHARACTER_WIDTH_MULTIPLIER = 0.6;
|
|
1937
2025
|
var TEXT_INTERSECTION_PADDING_MULTIPLIER = 0.3;
|
|
@@ -2064,11 +2152,11 @@ function drawDimensionLine(params) {
|
|
|
2064
2152
|
x: (from.x + to.x) / 2 + offsetVector.x,
|
|
2065
2153
|
y: (from.y + to.y) / 2 + offsetVector.y
|
|
2066
2154
|
};
|
|
2067
|
-
const [screenFromX, screenFromY] =
|
|
2155
|
+
const [screenFromX, screenFromY] = applyToPoint14(realToCanvasMat, [
|
|
2068
2156
|
fromOffset.x,
|
|
2069
2157
|
fromOffset.y
|
|
2070
2158
|
]);
|
|
2071
|
-
const [screenToX, screenToY] =
|
|
2159
|
+
const [screenToX, screenToY] = applyToPoint14(realToCanvasMat, [
|
|
2072
2160
|
toOffset.x,
|
|
2073
2161
|
toOffset.y
|
|
2074
2162
|
]);
|
|
@@ -2158,15 +2246,15 @@ function drawPcbFabricationNoteDimension(params) {
|
|
|
2158
2246
|
}
|
|
2159
2247
|
|
|
2160
2248
|
// lib/drawer/elements/pcb-note-line.ts
|
|
2161
|
-
import { applyToPoint as
|
|
2249
|
+
import { applyToPoint as applyToPoint15 } from "transformation-matrix";
|
|
2162
2250
|
function drawPcbNoteLine(params) {
|
|
2163
2251
|
const { ctx, line, realToCanvasMat, colorMap } = params;
|
|
2164
2252
|
const defaultColor = "rgb(89, 148, 220)";
|
|
2165
2253
|
const color = line.color ?? defaultColor;
|
|
2166
2254
|
const strokeWidth = line.stroke_width ?? 0.1;
|
|
2167
2255
|
const isDashed = line.is_dashed ?? false;
|
|
2168
|
-
const [x1, y1] =
|
|
2169
|
-
const [x2, y2] =
|
|
2256
|
+
const [x1, y1] = applyToPoint15(realToCanvasMat, [line.x1, line.y1]);
|
|
2257
|
+
const [x2, y2] = applyToPoint15(realToCanvasMat, [line.x2, line.y2]);
|
|
2170
2258
|
const scaledStrokeWidth = strokeWidth * Math.abs(realToCanvasMat.a);
|
|
2171
2259
|
ctx.save();
|
|
2172
2260
|
if (isDashed) {
|
|
@@ -2271,7 +2359,7 @@ var CircuitToCanvasDrawer = class {
|
|
|
2271
2359
|
if (outline && Array.isArray(outline) && outline.length >= 3) {
|
|
2272
2360
|
const soldermaskColor = this.colorMap.soldermask[layer] ?? this.colorMap.soldermask.top;
|
|
2273
2361
|
const canvasPoints = outline.map((p) => {
|
|
2274
|
-
const [x, y] =
|
|
2362
|
+
const [x, y] = applyToPoint16(this.realToCanvasMat, [p.x, p.y]);
|
|
2275
2363
|
return { x, y };
|
|
2276
2364
|
});
|
|
2277
2365
|
this.ctx.beginPath();
|
|
@@ -2442,6 +2530,14 @@ var CircuitToCanvasDrawer = class {
|
|
|
2442
2530
|
colorMap: this.colorMap
|
|
2443
2531
|
});
|
|
2444
2532
|
}
|
|
2533
|
+
if (element.type === "pcb_keepout") {
|
|
2534
|
+
drawPcbKeepout({
|
|
2535
|
+
ctx: this.ctx,
|
|
2536
|
+
keepout: element,
|
|
2537
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
2538
|
+
colorMap: this.colorMap
|
|
2539
|
+
});
|
|
2540
|
+
}
|
|
2445
2541
|
if (element.type === "pcb_copper_pour") {
|
|
2446
2542
|
drawPcbCopperPour({
|
|
2447
2543
|
ctx: this.ctx,
|
|
@@ -2566,6 +2662,7 @@ export {
|
|
|
2566
2662
|
drawPcbFabricationNoteRect,
|
|
2567
2663
|
drawPcbFabricationNoteText,
|
|
2568
2664
|
drawPcbHole,
|
|
2665
|
+
drawPcbKeepout,
|
|
2569
2666
|
drawPcbNoteDimension,
|
|
2570
2667
|
drawPcbNotePath,
|
|
2571
2668
|
drawPcbNoteRect,
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
PcbSilkscreenPath,
|
|
15
15
|
PcbSilkscreenPill,
|
|
16
16
|
PcbCutout,
|
|
17
|
+
PCBKeepout,
|
|
17
18
|
PcbCopperPour,
|
|
18
19
|
PcbCopperText,
|
|
19
20
|
PcbFabricationNoteText,
|
|
@@ -59,6 +60,7 @@ import { drawPcbSilkscreenPath } from "./elements/pcb-silkscreen-path"
|
|
|
59
60
|
import { drawPcbSilkscreenOval } from "./elements/pcb-silkscreen-oval"
|
|
60
61
|
import { drawPcbSilkscreenPill } from "./elements/pcb-silkscreen-pill"
|
|
61
62
|
import { drawPcbCutout } from "./elements/pcb-cutout"
|
|
63
|
+
import { drawPcbKeepout } from "./elements/pcb-keepout"
|
|
62
64
|
import { drawPcbCopperPour } from "./elements/pcb-copper-pour"
|
|
63
65
|
import { drawPcbCopperText } from "./elements/pcb-copper-text"
|
|
64
66
|
import { drawPcbFabricationNoteText } from "./elements/pcb-fabrication-note-text"
|
|
@@ -410,6 +412,15 @@ export class CircuitToCanvasDrawer {
|
|
|
410
412
|
})
|
|
411
413
|
}
|
|
412
414
|
|
|
415
|
+
if (element.type === "pcb_keepout") {
|
|
416
|
+
drawPcbKeepout({
|
|
417
|
+
ctx: this.ctx,
|
|
418
|
+
keepout: element as PCBKeepout,
|
|
419
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
420
|
+
colorMap: this.colorMap,
|
|
421
|
+
})
|
|
422
|
+
}
|
|
423
|
+
|
|
413
424
|
if (element.type === "pcb_copper_pour") {
|
|
414
425
|
drawPcbCopperPour({
|
|
415
426
|
ctx: this.ctx,
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { PCBKeepout } from "circuit-json"
|
|
2
|
+
import type { Matrix } from "transformation-matrix"
|
|
3
|
+
import { applyToPoint } from "transformation-matrix"
|
|
4
|
+
import type { PcbColorMap, CanvasContext } from "../types"
|
|
5
|
+
|
|
6
|
+
export interface DrawPcbKeepoutParams {
|
|
7
|
+
ctx: CanvasContext
|
|
8
|
+
keepout: PCBKeepout
|
|
9
|
+
realToCanvasMat: Matrix
|
|
10
|
+
colorMap: PcbColorMap
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts hex color to rgba with transparency
|
|
15
|
+
*/
|
|
16
|
+
function hexToRgba(hex: string, alpha: number): string {
|
|
17
|
+
const r = parseInt(hex.slice(1, 3), 16)
|
|
18
|
+
const g = parseInt(hex.slice(3, 5), 16)
|
|
19
|
+
const b = parseInt(hex.slice(5, 7), 16)
|
|
20
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function drawPcbKeepout(params: DrawPcbKeepoutParams): void {
|
|
24
|
+
const { ctx, keepout, realToCanvasMat, colorMap } = params
|
|
25
|
+
|
|
26
|
+
// Keepout zones are shown with transparent red background and diagonal red lines pattern
|
|
27
|
+
const strokeColor = colorMap.keepout
|
|
28
|
+
const fillColor = hexToRgba(colorMap.keepout, 0.2) // Slight transparent red background
|
|
29
|
+
const hatchSpacing = 1.0 // Spacing between diagonal lines
|
|
30
|
+
|
|
31
|
+
if (keepout.shape === "rect") {
|
|
32
|
+
const [cx, cy] = applyToPoint(realToCanvasMat, [
|
|
33
|
+
keepout.center.x,
|
|
34
|
+
keepout.center.y,
|
|
35
|
+
])
|
|
36
|
+
const scaledWidth = keepout.width * Math.abs(realToCanvasMat.a)
|
|
37
|
+
const scaledHeight = keepout.height * Math.abs(realToCanvasMat.a)
|
|
38
|
+
const rotation = (keepout as { rotation?: number }).rotation ?? 0
|
|
39
|
+
|
|
40
|
+
ctx.save()
|
|
41
|
+
ctx.translate(cx, cy)
|
|
42
|
+
|
|
43
|
+
if (rotation !== 0) {
|
|
44
|
+
ctx.rotate(-rotation * (Math.PI / 180))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Draw transparent red background
|
|
48
|
+
ctx.beginPath()
|
|
49
|
+
ctx.rect(-scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight)
|
|
50
|
+
ctx.fillStyle = fillColor
|
|
51
|
+
ctx.fill()
|
|
52
|
+
|
|
53
|
+
// Set up clipping path for the rectangle
|
|
54
|
+
ctx.beginPath()
|
|
55
|
+
ctx.rect(-scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight)
|
|
56
|
+
ctx.clip()
|
|
57
|
+
|
|
58
|
+
// Draw diagonal lines pattern at 45 degrees
|
|
59
|
+
const scaledSpacing = hatchSpacing * Math.abs(realToCanvasMat.a)
|
|
60
|
+
const diagonal = Math.sqrt(
|
|
61
|
+
scaledWidth * scaledWidth + scaledHeight * scaledHeight,
|
|
62
|
+
)
|
|
63
|
+
const halfWidth = scaledWidth / 2
|
|
64
|
+
const halfHeight = scaledHeight / 2
|
|
65
|
+
|
|
66
|
+
ctx.strokeStyle = strokeColor
|
|
67
|
+
ctx.lineWidth = 0.15 * Math.abs(realToCanvasMat.a)
|
|
68
|
+
ctx.setLineDash([])
|
|
69
|
+
|
|
70
|
+
// Draw diagonal lines from top-left to bottom-right
|
|
71
|
+
for (
|
|
72
|
+
let offset = -diagonal;
|
|
73
|
+
offset < diagonal * 2;
|
|
74
|
+
offset += scaledSpacing
|
|
75
|
+
) {
|
|
76
|
+
ctx.beginPath()
|
|
77
|
+
// Line goes from left edge to right edge (or top to bottom)
|
|
78
|
+
const startX = -halfWidth + offset
|
|
79
|
+
const startY = -halfHeight
|
|
80
|
+
const endX = -halfWidth + offset + diagonal
|
|
81
|
+
const endY = -halfHeight + diagonal
|
|
82
|
+
|
|
83
|
+
ctx.moveTo(startX, startY)
|
|
84
|
+
ctx.lineTo(endX, endY)
|
|
85
|
+
ctx.stroke()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
ctx.restore()
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (keepout.shape === "circle") {
|
|
93
|
+
const [cx, cy] = applyToPoint(realToCanvasMat, [
|
|
94
|
+
keepout.center.x,
|
|
95
|
+
keepout.center.y,
|
|
96
|
+
])
|
|
97
|
+
const scaledRadius = keepout.radius * Math.abs(realToCanvasMat.a)
|
|
98
|
+
const scaledSpacing = hatchSpacing * Math.abs(realToCanvasMat.a)
|
|
99
|
+
|
|
100
|
+
ctx.save()
|
|
101
|
+
ctx.translate(cx, cy)
|
|
102
|
+
|
|
103
|
+
// Draw transparent red background
|
|
104
|
+
ctx.beginPath()
|
|
105
|
+
ctx.arc(0, 0, scaledRadius, 0, Math.PI * 2)
|
|
106
|
+
ctx.fillStyle = fillColor
|
|
107
|
+
ctx.fill()
|
|
108
|
+
|
|
109
|
+
// Set up clipping path for the circle
|
|
110
|
+
ctx.beginPath()
|
|
111
|
+
ctx.arc(0, 0, scaledRadius, 0, Math.PI * 2)
|
|
112
|
+
ctx.clip()
|
|
113
|
+
|
|
114
|
+
// Draw diagonal lines pattern at 45 degrees
|
|
115
|
+
const diagonal = scaledRadius * 2
|
|
116
|
+
|
|
117
|
+
ctx.strokeStyle = strokeColor
|
|
118
|
+
ctx.lineWidth = 0.15 * Math.abs(realToCanvasMat.a)
|
|
119
|
+
ctx.setLineDash([])
|
|
120
|
+
|
|
121
|
+
// Draw diagonal lines from top-left to bottom-right
|
|
122
|
+
for (
|
|
123
|
+
let offset = -diagonal;
|
|
124
|
+
offset < diagonal * 2;
|
|
125
|
+
offset += scaledSpacing
|
|
126
|
+
) {
|
|
127
|
+
ctx.beginPath()
|
|
128
|
+
ctx.moveTo(offset - diagonal, -diagonal)
|
|
129
|
+
ctx.lineTo(offset + diagonal, diagonal)
|
|
130
|
+
ctx.stroke()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
ctx.restore()
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -46,13 +46,6 @@ export function drawRect(params: DrawRectParams): void {
|
|
|
46
46
|
ctx.rotate(-rotation * (Math.PI / 180))
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// Set up dashed line if needed
|
|
50
|
-
if (isStrokeDashed && scaledStrokeWidth) {
|
|
51
|
-
ctx.setLineDash([scaledStrokeWidth * 2, scaledStrokeWidth * 2])
|
|
52
|
-
} else {
|
|
53
|
-
ctx.setLineDash([])
|
|
54
|
-
}
|
|
55
|
-
|
|
56
49
|
ctx.beginPath()
|
|
57
50
|
|
|
58
51
|
if (scaledRadius > 0) {
|
|
@@ -85,6 +78,12 @@ export function drawRect(params: DrawRectParams): void {
|
|
|
85
78
|
}
|
|
86
79
|
|
|
87
80
|
if (stroke && scaledStrokeWidth) {
|
|
81
|
+
// Set up dashed line if needed (after path is drawn, before stroke)
|
|
82
|
+
if (isStrokeDashed) {
|
|
83
|
+
ctx.setLineDash([scaledStrokeWidth * 3, scaledStrokeWidth * 2])
|
|
84
|
+
} else {
|
|
85
|
+
ctx.setLineDash([])
|
|
86
|
+
}
|
|
88
87
|
ctx.strokeStyle = stroke
|
|
89
88
|
ctx.lineWidth = scaledStrokeWidth
|
|
90
89
|
ctx.stroke()
|
package/lib/drawer/types.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface CanvasContext {
|
|
|
31
31
|
moveTo(x: number, y: number): void
|
|
32
32
|
save(): void
|
|
33
33
|
restore(): void
|
|
34
|
+
clip(): void
|
|
34
35
|
translate(x: number, y: number): void
|
|
35
36
|
rotate(angle: number): void
|
|
36
37
|
scale(x: number, y: number): void
|
|
@@ -89,6 +90,7 @@ export interface PcbColorMap {
|
|
|
89
90
|
}
|
|
90
91
|
substrate: string
|
|
91
92
|
courtyard: string
|
|
93
|
+
keepout: string
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
export const DEFAULT_PCB_COLOR_MAP: PcbColorMap = {
|
|
@@ -122,6 +124,7 @@ export const DEFAULT_PCB_COLOR_MAP: PcbColorMap = {
|
|
|
122
124
|
},
|
|
123
125
|
boardOutline: "rgba(255, 255, 255, 0.5)",
|
|
124
126
|
courtyard: "#FF00FF",
|
|
127
|
+
keepout: "#FF6B6B", // Red color for keepout zones
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
export interface DrawerConfig {
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
+
import type { AnyCircuitElement } from "circuit-json"
|
|
4
|
+
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
5
|
+
|
|
6
|
+
test("pcb keepout with layer filter", async () => {
|
|
7
|
+
const canvas = createCanvas(2000, 1600)
|
|
8
|
+
const ctx = canvas.getContext("2d")
|
|
9
|
+
const drawer = new CircuitToCanvasDrawer(ctx)
|
|
10
|
+
|
|
11
|
+
drawer.setCameraBounds({ minX: -25, maxX: 25, minY: -20, maxY: 20 })
|
|
12
|
+
|
|
13
|
+
ctx.fillStyle = "#1a1a1a"
|
|
14
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
15
|
+
|
|
16
|
+
const elements: AnyCircuitElement[] = [
|
|
17
|
+
{
|
|
18
|
+
type: "pcb_board",
|
|
19
|
+
pcb_board_id: "pcb_board_0",
|
|
20
|
+
center: { x: 0, y: 0 },
|
|
21
|
+
width: 50,
|
|
22
|
+
height: 40,
|
|
23
|
+
material: "fr1",
|
|
24
|
+
num_layers: 2,
|
|
25
|
+
thickness: 1.2,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: "pcb_keepout",
|
|
29
|
+
shape: "rect",
|
|
30
|
+
pcb_keepout_id: "pcb_keepout_top",
|
|
31
|
+
center: { x: -10, y: 0 },
|
|
32
|
+
width: 8,
|
|
33
|
+
height: 8,
|
|
34
|
+
layers: ["top"],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: "pcb_keepout",
|
|
38
|
+
shape: "circle",
|
|
39
|
+
pcb_keepout_id: "pcb_keepout_bottom",
|
|
40
|
+
center: { x: 10, y: 0 },
|
|
41
|
+
radius: 4,
|
|
42
|
+
layers: ["bottom"],
|
|
43
|
+
},
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
drawer.drawElements(elements, { layers: ["top_copper"] })
|
|
47
|
+
|
|
48
|
+
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
49
|
+
import.meta.path,
|
|
50
|
+
)
|
|
51
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
+
import type { AnyCircuitElement } from "circuit-json"
|
|
4
|
+
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
5
|
+
|
|
6
|
+
test("pcb keepout multiple layers", async () => {
|
|
7
|
+
const canvas = createCanvas(2000, 1600)
|
|
8
|
+
const ctx = canvas.getContext("2d")
|
|
9
|
+
const drawer = new CircuitToCanvasDrawer(ctx)
|
|
10
|
+
|
|
11
|
+
drawer.setCameraBounds({ minX: -25, maxX: 25, minY: -20, maxY: 20 })
|
|
12
|
+
|
|
13
|
+
ctx.fillStyle = "#1a1a1a"
|
|
14
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
15
|
+
|
|
16
|
+
const elements: AnyCircuitElement[] = [
|
|
17
|
+
{
|
|
18
|
+
type: "pcb_board",
|
|
19
|
+
pcb_board_id: "pcb_board_0",
|
|
20
|
+
center: { x: 0, y: 0 },
|
|
21
|
+
width: 50,
|
|
22
|
+
height: 40,
|
|
23
|
+
material: "fr1",
|
|
24
|
+
num_layers: 4,
|
|
25
|
+
thickness: 1.2,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: "pcb_keepout",
|
|
29
|
+
shape: "rect",
|
|
30
|
+
pcb_keepout_id: "pcb_keepout_multi_layer",
|
|
31
|
+
center: { x: 0, y: 0 },
|
|
32
|
+
width: 10,
|
|
33
|
+
height: 10,
|
|
34
|
+
layers: ["top", "bottom", "inner1"],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: "pcb_keepout",
|
|
38
|
+
shape: "circle",
|
|
39
|
+
pcb_keepout_id: "pcb_keepout_circle_multi",
|
|
40
|
+
center: { x: 15, y: 15 },
|
|
41
|
+
radius: 5,
|
|
42
|
+
layers: ["top", "bottom"],
|
|
43
|
+
},
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
drawer.drawElements(elements)
|
|
47
|
+
|
|
48
|
+
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
49
|
+
import.meta.path,
|
|
50
|
+
)
|
|
51
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
+
import type { AnyCircuitElement } from "circuit-json"
|
|
4
|
+
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
5
|
+
|
|
6
|
+
test("pcb keepout rect and circle", async () => {
|
|
7
|
+
const canvas = createCanvas(2000, 1600)
|
|
8
|
+
const ctx = canvas.getContext("2d")
|
|
9
|
+
const drawer = new CircuitToCanvasDrawer(ctx)
|
|
10
|
+
|
|
11
|
+
drawer.setCameraBounds({ minX: -25, maxX: 25, minY: -20, maxY: 20 })
|
|
12
|
+
|
|
13
|
+
ctx.fillStyle = "#1a1a1a"
|
|
14
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
15
|
+
|
|
16
|
+
const elements: AnyCircuitElement[] = [
|
|
17
|
+
{
|
|
18
|
+
type: "pcb_board",
|
|
19
|
+
pcb_board_id: "pcb_board_0",
|
|
20
|
+
center: { x: 0, y: 0 },
|
|
21
|
+
width: 50,
|
|
22
|
+
height: 40,
|
|
23
|
+
material: "fr1",
|
|
24
|
+
num_layers: 2,
|
|
25
|
+
thickness: 1.2,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: "pcb_keepout",
|
|
29
|
+
shape: "rect",
|
|
30
|
+
pcb_keepout_id: "pcb_keepout_rect_0",
|
|
31
|
+
center: { x: -10, y: 10 },
|
|
32
|
+
width: 8,
|
|
33
|
+
height: 5,
|
|
34
|
+
layers: ["top"],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: "pcb_keepout",
|
|
38
|
+
shape: "circle",
|
|
39
|
+
pcb_keepout_id: "pcb_keepout_circle_0",
|
|
40
|
+
center: { x: 0, y: 0 },
|
|
41
|
+
radius: 4,
|
|
42
|
+
layers: ["top"],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: "pcb_keepout",
|
|
46
|
+
shape: "rect",
|
|
47
|
+
pcb_keepout_id: "pcb_keepout_rect_1",
|
|
48
|
+
center: { x: 10, y: -10 },
|
|
49
|
+
width: 6,
|
|
50
|
+
height: 6,
|
|
51
|
+
layers: ["bottom"],
|
|
52
|
+
},
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
drawer.drawElements(elements)
|
|
56
|
+
|
|
57
|
+
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
58
|
+
import.meta.path,
|
|
59
|
+
)
|
|
60
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas"
|
|
3
|
+
import type { AnyCircuitElement } from "circuit-json"
|
|
4
|
+
import { CircuitToCanvasDrawer } from "../../lib/drawer"
|
|
5
|
+
|
|
6
|
+
test("pcb keepout with pcb_group_id and subcircuit_id", async () => {
|
|
7
|
+
const canvas = createCanvas(2000, 1600)
|
|
8
|
+
const ctx = canvas.getContext("2d")
|
|
9
|
+
const drawer = new CircuitToCanvasDrawer(ctx)
|
|
10
|
+
|
|
11
|
+
drawer.setCameraBounds({ minX: -25, maxX: 25, minY: -20, maxY: 20 })
|
|
12
|
+
|
|
13
|
+
ctx.fillStyle = "#1a1a1a"
|
|
14
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height)
|
|
15
|
+
|
|
16
|
+
const elements: AnyCircuitElement[] = [
|
|
17
|
+
{
|
|
18
|
+
type: "pcb_board",
|
|
19
|
+
pcb_board_id: "pcb_board_0",
|
|
20
|
+
center: { x: 0, y: 0 },
|
|
21
|
+
width: 50,
|
|
22
|
+
height: 40,
|
|
23
|
+
material: "fr1",
|
|
24
|
+
num_layers: 2,
|
|
25
|
+
thickness: 1.2,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: "pcb_keepout",
|
|
29
|
+
shape: "rect",
|
|
30
|
+
pcb_keepout_id: "pcb_keepout_grouped",
|
|
31
|
+
center: { x: 0, y: 0 },
|
|
32
|
+
width: 10,
|
|
33
|
+
height: 10,
|
|
34
|
+
layers: ["top"],
|
|
35
|
+
pcb_group_id: "group_1",
|
|
36
|
+
subcircuit_id: "subcircuit_1",
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
drawer.drawElements(elements)
|
|
41
|
+
|
|
42
|
+
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
43
|
+
import.meta.path,
|
|
44
|
+
)
|
|
45
|
+
})
|