circuit-to-canvas 0.0.41 → 0.0.43
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 +127 -27
- 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/elements/pcb-smtpad.ts +16 -30
- 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();
|
|
@@ -955,6 +957,9 @@ function layerToColor(layer, colorMap) {
|
|
|
955
957
|
function getSoldermaskColor2(layer, colorMap) {
|
|
956
958
|
return colorMap.soldermaskOverCopper[layer] ?? colorMap.soldermaskOverCopper.top;
|
|
957
959
|
}
|
|
960
|
+
function getBorderRadius(pad, margin = 0) {
|
|
961
|
+
return (pad.corner_radius ?? pad.rect_border_radius ?? 0) + margin;
|
|
962
|
+
}
|
|
958
963
|
function drawPcbSmtPad(params) {
|
|
959
964
|
const { ctx, pad, realToCanvasMat, colorMap } = params;
|
|
960
965
|
const color = layerToColor(pad.layer, colorMap);
|
|
@@ -973,7 +978,7 @@ function drawPcbSmtPad(params) {
|
|
|
973
978
|
height: pad.height + margin * 2,
|
|
974
979
|
fill: positiveMarginColor,
|
|
975
980
|
realToCanvasMat,
|
|
976
|
-
borderRadius: (pad
|
|
981
|
+
borderRadius: getBorderRadius(pad, margin)
|
|
977
982
|
});
|
|
978
983
|
}
|
|
979
984
|
drawRect({
|
|
@@ -983,7 +988,7 @@ function drawPcbSmtPad(params) {
|
|
|
983
988
|
height: pad.height,
|
|
984
989
|
fill: color,
|
|
985
990
|
realToCanvasMat,
|
|
986
|
-
borderRadius: pad
|
|
991
|
+
borderRadius: getBorderRadius(pad)
|
|
987
992
|
});
|
|
988
993
|
if (hasSoldermask && margin < 0) {
|
|
989
994
|
drawSoldermaskRingForRect(
|
|
@@ -992,7 +997,7 @@ function drawPcbSmtPad(params) {
|
|
|
992
997
|
pad.width,
|
|
993
998
|
pad.height,
|
|
994
999
|
margin,
|
|
995
|
-
pad
|
|
1000
|
+
getBorderRadius(pad),
|
|
996
1001
|
0,
|
|
997
1002
|
realToCanvasMat,
|
|
998
1003
|
soldermaskRingColor,
|
|
@@ -1007,7 +1012,7 @@ function drawPcbSmtPad(params) {
|
|
|
1007
1012
|
height: pad.height,
|
|
1008
1013
|
fill: soldermaskOverlayColor,
|
|
1009
1014
|
realToCanvasMat,
|
|
1010
|
-
borderRadius: pad
|
|
1015
|
+
borderRadius: getBorderRadius(pad)
|
|
1011
1016
|
});
|
|
1012
1017
|
}
|
|
1013
1018
|
return;
|
|
@@ -1021,7 +1026,7 @@ function drawPcbSmtPad(params) {
|
|
|
1021
1026
|
height: pad.height + margin * 2,
|
|
1022
1027
|
fill: positiveMarginColor,
|
|
1023
1028
|
realToCanvasMat,
|
|
1024
|
-
borderRadius: (pad
|
|
1029
|
+
borderRadius: getBorderRadius(pad, margin),
|
|
1025
1030
|
rotation: pad.ccw_rotation ?? 0
|
|
1026
1031
|
});
|
|
1027
1032
|
}
|
|
@@ -1032,7 +1037,7 @@ function drawPcbSmtPad(params) {
|
|
|
1032
1037
|
height: pad.height,
|
|
1033
1038
|
fill: color,
|
|
1034
1039
|
realToCanvasMat,
|
|
1035
|
-
borderRadius: pad
|
|
1040
|
+
borderRadius: getBorderRadius(pad),
|
|
1036
1041
|
rotation: pad.ccw_rotation ?? 0
|
|
1037
1042
|
});
|
|
1038
1043
|
if (hasSoldermask && margin < 0) {
|
|
@@ -1042,7 +1047,7 @@ function drawPcbSmtPad(params) {
|
|
|
1042
1047
|
pad.width,
|
|
1043
1048
|
pad.height,
|
|
1044
1049
|
margin,
|
|
1045
|
-
pad
|
|
1050
|
+
getBorderRadius(pad),
|
|
1046
1051
|
pad.ccw_rotation ?? 0,
|
|
1047
1052
|
realToCanvasMat,
|
|
1048
1053
|
soldermaskRingColor,
|
|
@@ -1057,7 +1062,7 @@ function drawPcbSmtPad(params) {
|
|
|
1057
1062
|
height: pad.height,
|
|
1058
1063
|
fill: soldermaskOverlayColor,
|
|
1059
1064
|
realToCanvasMat,
|
|
1060
|
-
borderRadius: pad
|
|
1065
|
+
borderRadius: getBorderRadius(pad),
|
|
1061
1066
|
rotation: pad.ccw_rotation ?? 0
|
|
1062
1067
|
});
|
|
1063
1068
|
}
|
|
@@ -1673,8 +1678,94 @@ function drawPcbCutout(params) {
|
|
|
1673
1678
|
}
|
|
1674
1679
|
}
|
|
1675
1680
|
|
|
1676
|
-
// lib/drawer/elements/pcb-
|
|
1681
|
+
// lib/drawer/elements/pcb-keepout.ts
|
|
1677
1682
|
import { applyToPoint as applyToPoint11 } from "transformation-matrix";
|
|
1683
|
+
function hexToRgba(hex, alpha) {
|
|
1684
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
1685
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
1686
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
1687
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
1688
|
+
}
|
|
1689
|
+
function drawPcbKeepout(params) {
|
|
1690
|
+
const { ctx, keepout, realToCanvasMat, colorMap } = params;
|
|
1691
|
+
const strokeColor = colorMap.keepout;
|
|
1692
|
+
const fillColor = hexToRgba(colorMap.keepout, 0.2);
|
|
1693
|
+
const hatchSpacing = 1;
|
|
1694
|
+
if (keepout.shape === "rect") {
|
|
1695
|
+
const [cx, cy] = applyToPoint11(realToCanvasMat, [
|
|
1696
|
+
keepout.center.x,
|
|
1697
|
+
keepout.center.y
|
|
1698
|
+
]);
|
|
1699
|
+
const scaledWidth = keepout.width * Math.abs(realToCanvasMat.a);
|
|
1700
|
+
const scaledHeight = keepout.height * Math.abs(realToCanvasMat.a);
|
|
1701
|
+
const rotation = keepout.rotation ?? 0;
|
|
1702
|
+
ctx.save();
|
|
1703
|
+
ctx.translate(cx, cy);
|
|
1704
|
+
if (rotation !== 0) {
|
|
1705
|
+
ctx.rotate(-rotation * (Math.PI / 180));
|
|
1706
|
+
}
|
|
1707
|
+
ctx.beginPath();
|
|
1708
|
+
ctx.rect(-scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight);
|
|
1709
|
+
ctx.fillStyle = fillColor;
|
|
1710
|
+
ctx.fill();
|
|
1711
|
+
ctx.beginPath();
|
|
1712
|
+
ctx.rect(-scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight);
|
|
1713
|
+
ctx.clip();
|
|
1714
|
+
const scaledSpacing = hatchSpacing * Math.abs(realToCanvasMat.a);
|
|
1715
|
+
const diagonal = Math.sqrt(
|
|
1716
|
+
scaledWidth * scaledWidth + scaledHeight * scaledHeight
|
|
1717
|
+
);
|
|
1718
|
+
const halfWidth = scaledWidth / 2;
|
|
1719
|
+
const halfHeight = scaledHeight / 2;
|
|
1720
|
+
ctx.strokeStyle = strokeColor;
|
|
1721
|
+
ctx.lineWidth = 0.15 * Math.abs(realToCanvasMat.a);
|
|
1722
|
+
ctx.setLineDash([]);
|
|
1723
|
+
for (let offset = -diagonal; offset < diagonal * 2; offset += scaledSpacing) {
|
|
1724
|
+
ctx.beginPath();
|
|
1725
|
+
const startX = -halfWidth + offset;
|
|
1726
|
+
const startY = -halfHeight;
|
|
1727
|
+
const endX = -halfWidth + offset + diagonal;
|
|
1728
|
+
const endY = -halfHeight + diagonal;
|
|
1729
|
+
ctx.moveTo(startX, startY);
|
|
1730
|
+
ctx.lineTo(endX, endY);
|
|
1731
|
+
ctx.stroke();
|
|
1732
|
+
}
|
|
1733
|
+
ctx.restore();
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
if (keepout.shape === "circle") {
|
|
1737
|
+
const [cx, cy] = applyToPoint11(realToCanvasMat, [
|
|
1738
|
+
keepout.center.x,
|
|
1739
|
+
keepout.center.y
|
|
1740
|
+
]);
|
|
1741
|
+
const scaledRadius = keepout.radius * Math.abs(realToCanvasMat.a);
|
|
1742
|
+
const scaledSpacing = hatchSpacing * Math.abs(realToCanvasMat.a);
|
|
1743
|
+
ctx.save();
|
|
1744
|
+
ctx.translate(cx, cy);
|
|
1745
|
+
ctx.beginPath();
|
|
1746
|
+
ctx.arc(0, 0, scaledRadius, 0, Math.PI * 2);
|
|
1747
|
+
ctx.fillStyle = fillColor;
|
|
1748
|
+
ctx.fill();
|
|
1749
|
+
ctx.beginPath();
|
|
1750
|
+
ctx.arc(0, 0, scaledRadius, 0, Math.PI * 2);
|
|
1751
|
+
ctx.clip();
|
|
1752
|
+
const diagonal = scaledRadius * 2;
|
|
1753
|
+
ctx.strokeStyle = strokeColor;
|
|
1754
|
+
ctx.lineWidth = 0.15 * Math.abs(realToCanvasMat.a);
|
|
1755
|
+
ctx.setLineDash([]);
|
|
1756
|
+
for (let offset = -diagonal; offset < diagonal * 2; offset += scaledSpacing) {
|
|
1757
|
+
ctx.beginPath();
|
|
1758
|
+
ctx.moveTo(offset - diagonal, -diagonal);
|
|
1759
|
+
ctx.lineTo(offset + diagonal, diagonal);
|
|
1760
|
+
ctx.stroke();
|
|
1761
|
+
}
|
|
1762
|
+
ctx.restore();
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
// lib/drawer/elements/pcb-copper-pour.ts
|
|
1768
|
+
import { applyToPoint as applyToPoint12 } from "transformation-matrix";
|
|
1678
1769
|
function layerToColor3(layer, colorMap) {
|
|
1679
1770
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
1680
1771
|
}
|
|
@@ -1683,7 +1774,7 @@ function drawPcbCopperPour(params) {
|
|
|
1683
1774
|
const color = layerToColor3(pour.layer, colorMap);
|
|
1684
1775
|
ctx.save();
|
|
1685
1776
|
if (pour.shape === "rect") {
|
|
1686
|
-
const [cx, cy] =
|
|
1777
|
+
const [cx, cy] = applyToPoint12(realToCanvasMat, [
|
|
1687
1778
|
pour.center.x,
|
|
1688
1779
|
pour.center.y
|
|
1689
1780
|
]);
|
|
@@ -1704,7 +1795,7 @@ function drawPcbCopperPour(params) {
|
|
|
1704
1795
|
if (pour.shape === "polygon") {
|
|
1705
1796
|
if (pour.points && pour.points.length >= 3) {
|
|
1706
1797
|
const canvasPoints = pour.points.map(
|
|
1707
|
-
(p) =>
|
|
1798
|
+
(p) => applyToPoint12(realToCanvasMat, [p.x, p.y])
|
|
1708
1799
|
);
|
|
1709
1800
|
const firstPoint = canvasPoints[0];
|
|
1710
1801
|
if (!firstPoint) {
|
|
@@ -1732,7 +1823,7 @@ function drawPcbCopperPour(params) {
|
|
|
1732
1823
|
}
|
|
1733
1824
|
|
|
1734
1825
|
// lib/drawer/elements/pcb-copper-text.ts
|
|
1735
|
-
import { applyToPoint as
|
|
1826
|
+
import { applyToPoint as applyToPoint13 } from "transformation-matrix";
|
|
1736
1827
|
var DEFAULT_PADDING = { left: 0.2, right: 0.2, top: 0.2, bottom: 0.2 };
|
|
1737
1828
|
function layerToCopperColor(layer, colorMap) {
|
|
1738
1829
|
return colorMap.copper[layer] ?? colorMap.copper.top;
|
|
@@ -1747,7 +1838,7 @@ function drawPcbCopperText(params) {
|
|
|
1747
1838
|
const { ctx, text, realToCanvasMat, colorMap } = params;
|
|
1748
1839
|
const content = text.text ?? "";
|
|
1749
1840
|
if (!content) return;
|
|
1750
|
-
const [x, y] =
|
|
1841
|
+
const [x, y] = applyToPoint13(realToCanvasMat, [
|
|
1751
1842
|
text.anchor_position.x,
|
|
1752
1843
|
text.anchor_position.y
|
|
1753
1844
|
]);
|
|
@@ -1931,7 +2022,7 @@ function drawPcbNoteText(params) {
|
|
|
1931
2022
|
}
|
|
1932
2023
|
|
|
1933
2024
|
// lib/drawer/shapes/dimension-line.ts
|
|
1934
|
-
import { applyToPoint as
|
|
2025
|
+
import { applyToPoint as applyToPoint14 } from "transformation-matrix";
|
|
1935
2026
|
var TEXT_OFFSET_MULTIPLIER = 1.5;
|
|
1936
2027
|
var CHARACTER_WIDTH_MULTIPLIER = 0.6;
|
|
1937
2028
|
var TEXT_INTERSECTION_PADDING_MULTIPLIER = 0.3;
|
|
@@ -2064,11 +2155,11 @@ function drawDimensionLine(params) {
|
|
|
2064
2155
|
x: (from.x + to.x) / 2 + offsetVector.x,
|
|
2065
2156
|
y: (from.y + to.y) / 2 + offsetVector.y
|
|
2066
2157
|
};
|
|
2067
|
-
const [screenFromX, screenFromY] =
|
|
2158
|
+
const [screenFromX, screenFromY] = applyToPoint14(realToCanvasMat, [
|
|
2068
2159
|
fromOffset.x,
|
|
2069
2160
|
fromOffset.y
|
|
2070
2161
|
]);
|
|
2071
|
-
const [screenToX, screenToY] =
|
|
2162
|
+
const [screenToX, screenToY] = applyToPoint14(realToCanvasMat, [
|
|
2072
2163
|
toOffset.x,
|
|
2073
2164
|
toOffset.y
|
|
2074
2165
|
]);
|
|
@@ -2158,15 +2249,15 @@ function drawPcbFabricationNoteDimension(params) {
|
|
|
2158
2249
|
}
|
|
2159
2250
|
|
|
2160
2251
|
// lib/drawer/elements/pcb-note-line.ts
|
|
2161
|
-
import { applyToPoint as
|
|
2252
|
+
import { applyToPoint as applyToPoint15 } from "transformation-matrix";
|
|
2162
2253
|
function drawPcbNoteLine(params) {
|
|
2163
2254
|
const { ctx, line, realToCanvasMat, colorMap } = params;
|
|
2164
2255
|
const defaultColor = "rgb(89, 148, 220)";
|
|
2165
2256
|
const color = line.color ?? defaultColor;
|
|
2166
2257
|
const strokeWidth = line.stroke_width ?? 0.1;
|
|
2167
2258
|
const isDashed = line.is_dashed ?? false;
|
|
2168
|
-
const [x1, y1] =
|
|
2169
|
-
const [x2, y2] =
|
|
2259
|
+
const [x1, y1] = applyToPoint15(realToCanvasMat, [line.x1, line.y1]);
|
|
2260
|
+
const [x2, y2] = applyToPoint15(realToCanvasMat, [line.x2, line.y2]);
|
|
2170
2261
|
const scaledStrokeWidth = strokeWidth * Math.abs(realToCanvasMat.a);
|
|
2171
2262
|
ctx.save();
|
|
2172
2263
|
if (isDashed) {
|
|
@@ -2271,7 +2362,7 @@ var CircuitToCanvasDrawer = class {
|
|
|
2271
2362
|
if (outline && Array.isArray(outline) && outline.length >= 3) {
|
|
2272
2363
|
const soldermaskColor = this.colorMap.soldermask[layer] ?? this.colorMap.soldermask.top;
|
|
2273
2364
|
const canvasPoints = outline.map((p) => {
|
|
2274
|
-
const [x, y] =
|
|
2365
|
+
const [x, y] = applyToPoint16(this.realToCanvasMat, [p.x, p.y]);
|
|
2275
2366
|
return { x, y };
|
|
2276
2367
|
});
|
|
2277
2368
|
this.ctx.beginPath();
|
|
@@ -2442,6 +2533,14 @@ var CircuitToCanvasDrawer = class {
|
|
|
2442
2533
|
colorMap: this.colorMap
|
|
2443
2534
|
});
|
|
2444
2535
|
}
|
|
2536
|
+
if (element.type === "pcb_keepout") {
|
|
2537
|
+
drawPcbKeepout({
|
|
2538
|
+
ctx: this.ctx,
|
|
2539
|
+
keepout: element,
|
|
2540
|
+
realToCanvasMat: this.realToCanvasMat,
|
|
2541
|
+
colorMap: this.colorMap
|
|
2542
|
+
});
|
|
2543
|
+
}
|
|
2445
2544
|
if (element.type === "pcb_copper_pour") {
|
|
2446
2545
|
drawPcbCopperPour({
|
|
2447
2546
|
ctx: this.ctx,
|
|
@@ -2566,6 +2665,7 @@ export {
|
|
|
2566
2665
|
drawPcbFabricationNoteRect,
|
|
2567
2666
|
drawPcbFabricationNoteText,
|
|
2568
2667
|
drawPcbHole,
|
|
2668
|
+
drawPcbKeepout,
|
|
2569
2669
|
drawPcbNoteDimension,
|
|
2570
2670
|
drawPcbNotePath,
|
|
2571
2671
|
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
|
+
}
|
|
@@ -33,6 +33,14 @@ function getSoldermaskColor(layer: string, colorMap: PcbColorMap): string {
|
|
|
33
33
|
)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function getBorderRadius(pad: PcbSmtPad, margin: number = 0): number {
|
|
37
|
+
return (
|
|
38
|
+
((pad as { corner_radius?: number }).corner_radius ??
|
|
39
|
+
(pad as { rect_border_radius?: number }).rect_border_radius ??
|
|
40
|
+
0) + margin
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
37
45
|
const { ctx, pad, realToCanvasMat, colorMap } = params
|
|
38
46
|
|
|
@@ -62,10 +70,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
62
70
|
height: pad.height + margin * 2,
|
|
63
71
|
fill: positiveMarginColor,
|
|
64
72
|
realToCanvasMat,
|
|
65
|
-
borderRadius:
|
|
66
|
-
((pad as { corner_radius?: number }).corner_radius ??
|
|
67
|
-
pad.rect_border_radius ??
|
|
68
|
-
0) + margin,
|
|
73
|
+
borderRadius: getBorderRadius(pad, margin),
|
|
69
74
|
})
|
|
70
75
|
}
|
|
71
76
|
|
|
@@ -77,10 +82,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
77
82
|
height: pad.height,
|
|
78
83
|
fill: color,
|
|
79
84
|
realToCanvasMat,
|
|
80
|
-
borderRadius:
|
|
81
|
-
(pad as { corner_radius?: number }).corner_radius ??
|
|
82
|
-
pad.rect_border_radius ??
|
|
83
|
-
0,
|
|
85
|
+
borderRadius: getBorderRadius(pad),
|
|
84
86
|
})
|
|
85
87
|
|
|
86
88
|
// For negative margins, draw soldermask ring on top of the pad
|
|
@@ -91,9 +93,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
91
93
|
pad.width,
|
|
92
94
|
pad.height,
|
|
93
95
|
margin,
|
|
94
|
-
(pad
|
|
95
|
-
pad.rect_border_radius ??
|
|
96
|
-
0,
|
|
96
|
+
getBorderRadius(pad),
|
|
97
97
|
0,
|
|
98
98
|
realToCanvasMat,
|
|
99
99
|
soldermaskRingColor,
|
|
@@ -110,10 +110,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
110
110
|
height: pad.height,
|
|
111
111
|
fill: soldermaskOverlayColor,
|
|
112
112
|
realToCanvasMat,
|
|
113
|
-
borderRadius:
|
|
114
|
-
(pad as { corner_radius?: number }).corner_radius ??
|
|
115
|
-
pad.rect_border_radius ??
|
|
116
|
-
0,
|
|
113
|
+
borderRadius: getBorderRadius(pad),
|
|
117
114
|
})
|
|
118
115
|
}
|
|
119
116
|
return
|
|
@@ -129,10 +126,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
129
126
|
height: pad.height + margin * 2,
|
|
130
127
|
fill: positiveMarginColor,
|
|
131
128
|
realToCanvasMat,
|
|
132
|
-
borderRadius:
|
|
133
|
-
((pad as { corner_radius?: number }).corner_radius ??
|
|
134
|
-
pad.rect_border_radius ??
|
|
135
|
-
0) + margin,
|
|
129
|
+
borderRadius: getBorderRadius(pad, margin),
|
|
136
130
|
rotation: pad.ccw_rotation ?? 0,
|
|
137
131
|
})
|
|
138
132
|
}
|
|
@@ -145,10 +139,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
145
139
|
height: pad.height,
|
|
146
140
|
fill: color,
|
|
147
141
|
realToCanvasMat,
|
|
148
|
-
borderRadius:
|
|
149
|
-
(pad as { corner_radius?: number }).corner_radius ??
|
|
150
|
-
pad.rect_border_radius ??
|
|
151
|
-
0,
|
|
142
|
+
borderRadius: getBorderRadius(pad),
|
|
152
143
|
rotation: pad.ccw_rotation ?? 0,
|
|
153
144
|
})
|
|
154
145
|
|
|
@@ -160,9 +151,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
160
151
|
pad.width,
|
|
161
152
|
pad.height,
|
|
162
153
|
margin,
|
|
163
|
-
(pad
|
|
164
|
-
pad.rect_border_radius ??
|
|
165
|
-
0,
|
|
154
|
+
getBorderRadius(pad),
|
|
166
155
|
pad.ccw_rotation ?? 0,
|
|
167
156
|
realToCanvasMat,
|
|
168
157
|
soldermaskRingColor,
|
|
@@ -179,10 +168,7 @@ export function drawPcbSmtPad(params: DrawPcbSmtPadParams): void {
|
|
|
179
168
|
height: pad.height,
|
|
180
169
|
fill: soldermaskOverlayColor,
|
|
181
170
|
realToCanvasMat,
|
|
182
|
-
borderRadius:
|
|
183
|
-
(pad as { corner_radius?: number }).corner_radius ??
|
|
184
|
-
pad.rect_border_radius ??
|
|
185
|
-
0,
|
|
171
|
+
borderRadius: getBorderRadius(pad),
|
|
186
172
|
rotation: pad.ccw_rotation ?? 0,
|
|
187
173
|
})
|
|
188
174
|
}
|
|
@@ -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
|
+
})
|