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.
Files changed (24) hide show
  1. package/dist/index.d.ts +12 -2
  2. package/dist/index.js +127 -27
  3. package/lib/drawer/CircuitToCanvasDrawer.ts +11 -0
  4. package/lib/drawer/elements/index.ts +2 -0
  5. package/lib/drawer/elements/pcb-keepout.ts +136 -0
  6. package/lib/drawer/elements/pcb-smtpad.ts +16 -30
  7. package/lib/drawer/shapes/rect.ts +6 -7
  8. package/lib/drawer/types.ts +3 -0
  9. package/package.json +1 -1
  10. package/tests/elements/__snapshots__/multi-segment-trace.snap.png +0 -0
  11. package/tests/elements/__snapshots__/pcb-fabrication-note-rect-all-features.snap.png +0 -0
  12. package/tests/elements/__snapshots__/pcb-fabrication-note-rect-dashed.snap.png +0 -0
  13. package/tests/elements/__snapshots__/pcb-keepout-layer-filter.snap.png +0 -0
  14. package/tests/elements/__snapshots__/pcb-keepout-multiple-layers.snap.png +0 -0
  15. package/tests/elements/__snapshots__/pcb-keepout-rect-and-circle.snap.png +0 -0
  16. package/tests/elements/__snapshots__/pcb-keepout-with-group-id.snap.png +0 -0
  17. package/tests/elements/__snapshots__/pcb-note-rect-all-features.snap.png +0 -0
  18. package/tests/elements/__snapshots__/pcb-note-rect-dashed-stroke.snap.png +0 -0
  19. package/tests/elements/__snapshots__/pcb-smtpad-soldermask-coverage.snap.png +0 -0
  20. package/tests/elements/__snapshots__/with-group-id.snap.png +0 -0
  21. package/tests/elements/pcb-keepout-layer-filter.test.ts +51 -0
  22. package/tests/elements/pcb-keepout-multiple-layers.test.ts +51 -0
  23. package/tests/elements/pcb-keepout-rect-and-circle.test.ts +60 -0
  24. 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 applyToPoint15
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.corner_radius ?? pad.rect_border_radius ?? 0) + margin
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.corner_radius ?? pad.rect_border_radius ?? 0
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.corner_radius ?? pad.rect_border_radius ?? 0,
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.corner_radius ?? pad.rect_border_radius ?? 0
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.corner_radius ?? pad.rect_border_radius ?? 0) + margin,
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.corner_radius ?? pad.rect_border_radius ?? 0,
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.corner_radius ?? pad.rect_border_radius ?? 0,
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.corner_radius ?? pad.rect_border_radius ?? 0,
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-copper-pour.ts
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] = applyToPoint11(realToCanvasMat, [
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) => applyToPoint11(realToCanvasMat, [p.x, p.y])
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 applyToPoint12 } from "transformation-matrix";
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] = applyToPoint12(realToCanvasMat, [
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 applyToPoint13 } from "transformation-matrix";
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] = applyToPoint13(realToCanvasMat, [
2158
+ const [screenFromX, screenFromY] = applyToPoint14(realToCanvasMat, [
2068
2159
  fromOffset.x,
2069
2160
  fromOffset.y
2070
2161
  ]);
2071
- const [screenToX, screenToY] = applyToPoint13(realToCanvasMat, [
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 applyToPoint14 } from "transformation-matrix";
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] = applyToPoint14(realToCanvasMat, [line.x1, line.y1]);
2169
- const [x2, y2] = applyToPoint14(realToCanvasMat, [line.x2, line.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] = applyToPoint15(this.realToCanvasMat, [p.x, p.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,
@@ -56,6 +56,8 @@ export {
56
56
 
57
57
  export { drawPcbCutout, type DrawPcbCutoutParams } from "./pcb-cutout"
58
58
 
59
+ export { drawPcbKeepout, type DrawPcbKeepoutParams } from "./pcb-keepout"
60
+
59
61
  export {
60
62
  drawPcbCopperPour,
61
63
  type DrawPcbCopperPourParams,
@@ -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 as { corner_radius?: number }).corner_radius ??
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 as { corner_radius?: number }).corner_radius ??
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()
@@ -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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "circuit-to-canvas",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.41",
4
+ "version": "0.0.43",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "tsup-node ./lib/index.ts --format esm --dts",
@@ -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
+ })