circuit-to-canvas 0.0.40 → 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 +118 -20
- package/lib/drawer/CircuitToCanvasDrawer.ts +11 -0
- package/lib/drawer/elements/index.ts +2 -0
- package/lib/drawer/elements/pcb-cutout.ts +1 -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__/rect-cutout-with-corner-radius.snap.png +0 -0
- package/tests/elements/__snapshots__/with-group-id.snap.png +0 -0
- package/tests/elements/pcb-cutout.test.ts +24 -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();
|
|
@@ -1645,7 +1647,8 @@ function drawPcbCutout(params) {
|
|
|
1645
1647
|
height: cutout.height,
|
|
1646
1648
|
fill: colorMap.drill,
|
|
1647
1649
|
realToCanvasMat,
|
|
1648
|
-
rotation: cutout.rotation ?? 0
|
|
1650
|
+
rotation: cutout.rotation ?? 0,
|
|
1651
|
+
borderRadius: cutout.corner_radius ?? 0
|
|
1649
1652
|
});
|
|
1650
1653
|
return;
|
|
1651
1654
|
}
|
|
@@ -1672,8 +1675,94 @@ function drawPcbCutout(params) {
|
|
|
1672
1675
|
}
|
|
1673
1676
|
}
|
|
1674
1677
|
|
|
1675
|
-
// lib/drawer/elements/pcb-
|
|
1678
|
+
// lib/drawer/elements/pcb-keepout.ts
|
|
1676
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";
|
|
1677
1766
|
function layerToColor3(layer, colorMap) {
|
|
1678
1767
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
1679
1768
|
}
|
|
@@ -1682,7 +1771,7 @@ function drawPcbCopperPour(params) {
|
|
|
1682
1771
|
const color = layerToColor3(pour.layer, colorMap);
|
|
1683
1772
|
ctx.save();
|
|
1684
1773
|
if (pour.shape === "rect") {
|
|
1685
|
-
const [cx, cy] =
|
|
1774
|
+
const [cx, cy] = applyToPoint12(realToCanvasMat, [
|
|
1686
1775
|
pour.center.x,
|
|
1687
1776
|
pour.center.y
|
|
1688
1777
|
]);
|
|
@@ -1703,7 +1792,7 @@ function drawPcbCopperPour(params) {
|
|
|
1703
1792
|
if (pour.shape === "polygon") {
|
|
1704
1793
|
if (pour.points && pour.points.length >= 3) {
|
|
1705
1794
|
const canvasPoints = pour.points.map(
|
|
1706
|
-
(p) =>
|
|
1795
|
+
(p) => applyToPoint12(realToCanvasMat, [p.x, p.y])
|
|
1707
1796
|
);
|
|
1708
1797
|
const firstPoint = canvasPoints[0];
|
|
1709
1798
|
if (!firstPoint) {
|
|
@@ -1731,7 +1820,7 @@ function drawPcbCopperPour(params) {
|
|
|
1731
1820
|
}
|
|
1732
1821
|
|
|
1733
1822
|
// lib/drawer/elements/pcb-copper-text.ts
|
|
1734
|
-
import { applyToPoint as
|
|
1823
|
+
import { applyToPoint as applyToPoint13 } from "transformation-matrix";
|
|
1735
1824
|
var DEFAULT_PADDING = { left: 0.2, right: 0.2, top: 0.2, bottom: 0.2 };
|
|
1736
1825
|
function layerToCopperColor(layer, colorMap) {
|
|
1737
1826
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
@@ -1746,7 +1835,7 @@ function drawPcbCopperText(params) {
|
|
|
1746
1835
|
const { ctx, text, realToCanvasMat, colorMap } = params;
|
|
1747
1836
|
const content = text.text ?? "";
|
|
1748
1837
|
if (!content) return;
|
|
1749
|
-
const [x, y] =
|
|
1838
|
+
const [x, y] = applyToPoint13(realToCanvasMat, [
|
|
1750
1839
|
text.anchor_position.x,
|
|
1751
1840
|
text.anchor_position.y
|
|
1752
1841
|
]);
|
|
@@ -1930,7 +2019,7 @@ function drawPcbNoteText(params) {
|
|
|
1930
2019
|
}
|
|
1931
2020
|
|
|
1932
2021
|
// lib/drawer/shapes/dimension-line.ts
|
|
1933
|
-
import { applyToPoint as
|
|
2022
|
+
import { applyToPoint as applyToPoint14 } from "transformation-matrix";
|
|
1934
2023
|
var TEXT_OFFSET_MULTIPLIER = 1.5;
|
|
1935
2024
|
var CHARACTER_WIDTH_MULTIPLIER = 0.6;
|
|
1936
2025
|
var TEXT_INTERSECTION_PADDING_MULTIPLIER = 0.3;
|
|
@@ -2063,11 +2152,11 @@ function drawDimensionLine(params) {
|
|
|
2063
2152
|
x: (from.x + to.x) / 2 + offsetVector.x,
|
|
2064
2153
|
y: (from.y + to.y) / 2 + offsetVector.y
|
|
2065
2154
|
};
|
|
2066
|
-
const [screenFromX, screenFromY] =
|
|
2155
|
+
const [screenFromX, screenFromY] = applyToPoint14(realToCanvasMat, [
|
|
2067
2156
|
fromOffset.x,
|
|
2068
2157
|
fromOffset.y
|
|
2069
2158
|
]);
|
|
2070
|
-
const [screenToX, screenToY] =
|
|
2159
|
+
const [screenToX, screenToY] = applyToPoint14(realToCanvasMat, [
|
|
2071
2160
|
toOffset.x,
|
|
2072
2161
|
toOffset.y
|
|
2073
2162
|
]);
|
|
@@ -2157,15 +2246,15 @@ function drawPcbFabricationNoteDimension(params) {
|
|
|
2157
2246
|
}
|
|
2158
2247
|
|
|
2159
2248
|
// lib/drawer/elements/pcb-note-line.ts
|
|
2160
|
-
import { applyToPoint as
|
|
2249
|
+
import { applyToPoint as applyToPoint15 } from "transformation-matrix";
|
|
2161
2250
|
function drawPcbNoteLine(params) {
|
|
2162
2251
|
const { ctx, line, realToCanvasMat, colorMap } = params;
|
|
2163
2252
|
const defaultColor = "rgb(89, 148, 220)";
|
|
2164
2253
|
const color = line.color ?? defaultColor;
|
|
2165
2254
|
const strokeWidth = line.stroke_width ?? 0.1;
|
|
2166
2255
|
const isDashed = line.is_dashed ?? false;
|
|
2167
|
-
const [x1, y1] =
|
|
2168
|
-
const [x2, y2] =
|
|
2256
|
+
const [x1, y1] = applyToPoint15(realToCanvasMat, [line.x1, line.y1]);
|
|
2257
|
+
const [x2, y2] = applyToPoint15(realToCanvasMat, [line.x2, line.y2]);
|
|
2169
2258
|
const scaledStrokeWidth = strokeWidth * Math.abs(realToCanvasMat.a);
|
|
2170
2259
|
ctx.save();
|
|
2171
2260
|
if (isDashed) {
|
|
@@ -2270,7 +2359,7 @@ var CircuitToCanvasDrawer = class {
|
|
|
2270
2359
|
if (outline && Array.isArray(outline) && outline.length >= 3) {
|
|
2271
2360
|
const soldermaskColor = this.colorMap.soldermask[layer] ?? this.colorMap.soldermask.top;
|
|
2272
2361
|
const canvasPoints = outline.map((p) => {
|
|
2273
|
-
const [x, y] =
|
|
2362
|
+
const [x, y] = applyToPoint16(this.realToCanvasMat, [p.x, p.y]);
|
|
2274
2363
|
return { x, y };
|
|
2275
2364
|
});
|
|
2276
2365
|
this.ctx.beginPath();
|
|
@@ -2441,6 +2530,14 @@ var CircuitToCanvasDrawer = class {
|
|
|
2441
2530
|
colorMap: this.colorMap
|
|
2442
2531
|
});
|
|
2443
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
|
+
}
|
|
2444
2541
|
if (element.type === "pcb_copper_pour") {
|
|
2445
2542
|
drawPcbCopperPour({
|
|
2446
2543
|
ctx: this.ctx,
|
|
@@ -2565,6 +2662,7 @@ export {
|
|
|
2565
2662
|
drawPcbFabricationNoteRect,
|
|
2566
2663
|
drawPcbFabricationNoteText,
|
|
2567
2664
|
drawPcbHole,
|
|
2665
|
+
drawPcbKeepout,
|
|
2568
2666
|
drawPcbNoteDimension,
|
|
2569
2667
|
drawPcbNotePath,
|
|
2570
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
|
|
@@ -26,7 +26,31 @@ test("draw rectangular cutout", async () => {
|
|
|
26
26
|
import.meta.path,
|
|
27
27
|
)
|
|
28
28
|
})
|
|
29
|
+
test("draw rectangular cutout with corner radius", async () => {
|
|
30
|
+
const canvas = createCanvas(100, 100)
|
|
31
|
+
const ctx = canvas.getContext("2d")
|
|
32
|
+
const drawer = new CircuitToCanvasDrawer(ctx)
|
|
33
|
+
|
|
34
|
+
ctx.fillStyle = "#1a1a1a"
|
|
35
|
+
ctx.fillRect(0, 0, 100, 100)
|
|
29
36
|
|
|
37
|
+
const cutout: PcbCutout = {
|
|
38
|
+
type: "pcb_cutout",
|
|
39
|
+
pcb_cutout_id: "cutout1",
|
|
40
|
+
shape: "rect",
|
|
41
|
+
center: { x: 50, y: 50 },
|
|
42
|
+
width: 30,
|
|
43
|
+
height: 20,
|
|
44
|
+
corner_radius: 10,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
drawer.drawElements([cutout])
|
|
48
|
+
|
|
49
|
+
await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
|
|
50
|
+
import.meta.path,
|
|
51
|
+
"rect-cutout-with-corner-radius",
|
|
52
|
+
)
|
|
53
|
+
})
|
|
30
54
|
test("draw circular cutout", async () => {
|
|
31
55
|
const canvas = createCanvas(100, 100)
|
|
32
56
|
const ctx = canvas.getContext("2d")
|
|
@@ -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
|
+
})
|