circuit-to-canvas 0.0.32 → 0.0.34

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 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 } from 'circuit-json';
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';
2
2
  import { Matrix } from 'transformation-matrix';
3
3
 
4
4
  /**
@@ -221,6 +221,33 @@ interface DrawArrowParams {
221
221
  */
222
222
  declare function drawArrow(params: DrawArrowParams): void;
223
223
 
224
+ interface DrawDimensionLineParams {
225
+ ctx: CanvasContext;
226
+ from: {
227
+ x: number;
228
+ y: number;
229
+ };
230
+ to: {
231
+ x: number;
232
+ y: number;
233
+ };
234
+ realToCanvasMat: Matrix;
235
+ color: string;
236
+ fontSize: number;
237
+ arrowSize?: number;
238
+ strokeWidth?: number;
239
+ text?: string;
240
+ textRotation?: number;
241
+ offset?: {
242
+ distance: number;
243
+ direction: {
244
+ x: number;
245
+ y: number;
246
+ };
247
+ };
248
+ }
249
+ declare function drawDimensionLine(params: DrawDimensionLineParams): void;
250
+
224
251
  interface StrokeAlphabetTextParams {
225
252
  ctx: CanvasContext;
226
253
  text: string;
@@ -467,4 +494,12 @@ interface DrawPcbNoteDimensionParams {
467
494
  }
468
495
  declare function drawPcbNoteDimension(params: DrawPcbNoteDimensionParams): void;
469
496
 
470
- 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 DrawElementsOptions, type DrawLineParams, type DrawOvalParams, type DrawPathParams, type DrawPcbBoardParams, type DrawPcbCopperPourParams, type DrawPcbCopperTextParams, type DrawPcbCutoutParams, 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, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCopperText, drawPcbCutout, drawPcbFabricationNotePath, drawPcbFabricationNoteRect, drawPcbFabricationNoteText, drawPcbHole, drawPcbNoteDimension, drawPcbNotePath, drawPcbNoteRect, drawPcbNoteText, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenOval, drawPcbSilkscreenPath, drawPcbSilkscreenPill, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect, drawSoldermaskRingForCircle, drawSoldermaskRingForPill, drawSoldermaskRingForRect, drawText, getAlphabetLayout, getTextStartPosition, strokeAlphabetText };
497
+ interface DrawPcbFabricationNoteDimensionParams {
498
+ ctx: CanvasContext;
499
+ pcbFabricationNoteDimension: PcbFabricationNoteDimension;
500
+ realToCanvasMat: Matrix;
501
+ colorMap: PcbColorMap;
502
+ }
503
+ declare function drawPcbFabricationNoteDimension(params: DrawPcbFabricationNoteDimensionParams): void;
504
+
505
+ export { type AlphabetLayout, type AnchorAlignment, type CameraBounds, type CanvasContext, CircuitToCanvasDrawer, type CopperColorMap, type CopperLayerName, DEFAULT_PCB_COLOR_MAP, type DrawArrowParams, type DrawCircleParams, type DrawContext, type DrawDimensionLineParams, type DrawElementsOptions, type DrawLineParams, type DrawOvalParams, type DrawPathParams, type DrawPcbBoardParams, type DrawPcbCopperPourParams, type DrawPcbCopperTextParams, type DrawPcbCutoutParams, type DrawPcbFabricationNoteDimensionParams, type DrawPcbFabricationNotePathParams, type DrawPcbFabricationNoteRectParams, type DrawPcbFabricationNoteTextParams, type DrawPcbHoleParams, type DrawPcbNoteDimensionParams, type DrawPcbNotePathParams, type DrawPcbNoteRectParams, type DrawPcbNoteTextParams, type DrawPcbPlatedHoleParams, type DrawPcbSilkscreenCircleParams, type DrawPcbSilkscreenLineParams, type DrawPcbSilkscreenOvalParams, type DrawPcbSilkscreenPathParams, type DrawPcbSilkscreenPillParams, type DrawPcbSilkscreenRectParams, type DrawPcbSilkscreenTextParams, type DrawPcbSmtPadParams, type DrawPcbTraceParams, type DrawPcbViaParams, type DrawPillParams, type DrawPolygonParams, type DrawRectParams, type DrawTextParams, type DrawerConfig, type PcbColorMap, drawArrow, drawCircle, drawDimensionLine, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCopperText, drawPcbCutout, drawPcbFabricationNoteDimension, drawPcbFabricationNotePath, drawPcbFabricationNoteRect, drawPcbFabricationNoteText, drawPcbHole, drawPcbNoteDimension, drawPcbNotePath, drawPcbNoteRect, drawPcbNoteText, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenOval, drawPcbSilkscreenPath, drawPcbSilkscreenPill, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect, drawSoldermaskRingForCircle, drawSoldermaskRingForPill, drawSoldermaskRingForRect, drawText, getAlphabetLayout, getTextStartPosition, strokeAlphabetText };
package/dist/index.js CHANGED
@@ -1618,7 +1618,7 @@ function drawPcbNoteText(params) {
1618
1618
  });
1619
1619
  }
1620
1620
 
1621
- // lib/drawer/elements/pcb-note-dimension.ts
1621
+ // lib/drawer/shapes/dimension-line.ts
1622
1622
  import { applyToPoint as applyToPoint13 } from "transformation-matrix";
1623
1623
 
1624
1624
  // lib/drawer/shapes/arrow.ts
@@ -1640,134 +1640,202 @@ function drawArrow(params) {
1640
1640
  ctx.restore();
1641
1641
  }
1642
1642
 
1643
- // lib/drawer/elements/pcb-note-dimension.ts
1644
- var DEFAULT_NOTE_COLOR = "rgba(255,255,255,0.5)";
1645
- function drawPcbNoteDimension(params) {
1646
- const { ctx, pcbNoteDimension, realToCanvasMat } = params;
1647
- const color = pcbNoteDimension.color ?? DEFAULT_NOTE_COLOR;
1648
- const arrowSize = pcbNoteDimension.arrow_size;
1649
- const realFromX = pcbNoteDimension.from.x;
1650
- const realFromY = pcbNoteDimension.from.y;
1651
- const realToX = pcbNoteDimension.to.x;
1652
- const realToY = pcbNoteDimension.to.y;
1653
- let fromX = realFromX;
1654
- let fromY = realFromY;
1655
- let toX = realToX;
1656
- let toY = realToY;
1657
- let hasOffset = false;
1658
- let offsetX = 0;
1659
- let offsetY = 0;
1660
- if (pcbNoteDimension.offset_distance && pcbNoteDimension.offset_direction) {
1661
- const dirX = pcbNoteDimension.offset_direction.x;
1662
- const dirY = pcbNoteDimension.offset_direction.y;
1663
- const length = Math.hypot(dirX, dirY);
1664
- if (length > 0) {
1665
- const normX = dirX / length;
1666
- const normY = dirY / length;
1667
- hasOffset = true;
1668
- offsetX = pcbNoteDimension.offset_distance * normX;
1669
- offsetY = pcbNoteDimension.offset_distance * normY;
1670
- fromX += offsetX;
1671
- fromY += offsetY;
1672
- toX += offsetX;
1673
- toY += offsetY;
1674
- }
1675
- }
1676
- const STROKE_WIDTH_RATIO2 = 0.13;
1677
- const strokeWidth = Math.max(
1678
- pcbNoteDimension.font_size * STROKE_WIDTH_RATIO2,
1679
- 0.35
1680
- );
1681
- if (hasOffset) {
1682
- drawLine({
1683
- ctx,
1684
- start: { x: realFromX, y: realFromY },
1685
- end: { x: fromX, y: fromY },
1686
- strokeWidth,
1687
- stroke: color,
1688
- realToCanvasMat
1689
- });
1643
+ // lib/drawer/shapes/dimension-line.ts
1644
+ var TEXT_OFFSET_MULTIPLIER = 1.5;
1645
+ var CHARACTER_WIDTH_MULTIPLIER = 0.6;
1646
+ var TEXT_INTERSECTION_PADDING_MULTIPLIER = 0.3;
1647
+ function normalize(v) {
1648
+ const len = Math.hypot(v.x, v.y) || 1;
1649
+ return { x: v.x / len, y: v.y / len };
1650
+ }
1651
+ function drawDimensionLine(params) {
1652
+ const {
1653
+ ctx,
1654
+ from,
1655
+ to,
1656
+ realToCanvasMat,
1657
+ color,
1658
+ fontSize,
1659
+ arrowSize = 1,
1660
+ strokeWidth: manualStrokeWidth,
1661
+ text,
1662
+ textRotation,
1663
+ offset
1664
+ } = params;
1665
+ const direction = normalize({ x: to.x - from.x, y: to.y - from.y });
1666
+ const perpendicular = { x: -direction.y, y: direction.x };
1667
+ const hasOffsetDirection = offset?.direction && typeof offset.direction.x === "number" && typeof offset.direction.y === "number";
1668
+ const normalizedOffsetDirection = hasOffsetDirection ? normalize(offset.direction) : { x: 0, y: 0 };
1669
+ const offsetMagnitude = offset?.distance ?? 0;
1670
+ const offsetVector = {
1671
+ x: normalizedOffsetDirection.x * offsetMagnitude,
1672
+ y: normalizedOffsetDirection.y * offsetMagnitude
1673
+ };
1674
+ const fromOffset = { x: from.x + offsetVector.x, y: from.y + offsetVector.y };
1675
+ const toOffset = { x: to.x + offsetVector.x, y: to.y + offsetVector.y };
1676
+ const fromBase = fromOffset;
1677
+ const toBase = toOffset;
1678
+ const scaleValue = Math.abs(realToCanvasMat.a);
1679
+ const strokeWidth = manualStrokeWidth ?? arrowSize / 5;
1680
+ const lineColor = color || "rgba(255,255,255,0.5)";
1681
+ const extensionDirection = hasOffsetDirection && (Math.abs(normalizedOffsetDirection.x) > Number.EPSILON || Math.abs(normalizedOffsetDirection.y) > Number.EPSILON) ? normalizedOffsetDirection : perpendicular;
1682
+ const extensionLength = offsetMagnitude + 0.5;
1683
+ const drawExtension = (anchor) => {
1684
+ const endPoint = {
1685
+ x: anchor.x + extensionDirection.x * extensionLength,
1686
+ y: anchor.y + extensionDirection.y * extensionLength
1687
+ };
1690
1688
  drawLine({
1691
1689
  ctx,
1692
- start: { x: realToX, y: realToY },
1693
- end: { x: toX, y: toY },
1690
+ start: anchor,
1691
+ end: endPoint,
1694
1692
  strokeWidth,
1695
- stroke: color,
1693
+ stroke: lineColor,
1696
1694
  realToCanvasMat
1697
1695
  });
1698
- }
1696
+ };
1697
+ drawExtension(from);
1698
+ drawExtension(to);
1699
1699
  drawLine({
1700
1700
  ctx,
1701
- start: { x: fromX, y: fromY },
1702
- end: { x: toX, y: toY },
1701
+ start: fromBase,
1702
+ end: toBase,
1703
1703
  strokeWidth,
1704
- stroke: color,
1704
+ stroke: lineColor,
1705
1705
  realToCanvasMat
1706
1706
  });
1707
1707
  const [canvasFromX, canvasFromY] = applyToPoint13(realToCanvasMat, [
1708
- fromX,
1709
- fromY
1708
+ fromOffset.x,
1709
+ fromOffset.y
1710
1710
  ]);
1711
- const [canvasToX, canvasToY] = applyToPoint13(realToCanvasMat, [toX, toY]);
1712
- const canvasDx = canvasToX - canvasFromX;
1713
- const canvasDy = canvasToY - canvasFromY;
1714
- const lineAngle = Math.atan2(canvasDy, canvasDx);
1715
- const scale2 = Math.abs(realToCanvasMat.a);
1716
- const scaledArrowSize = arrowSize * scale2;
1717
- const scaledStrokeWidth = strokeWidth * scale2;
1711
+ const [canvasToX, canvasToY] = applyToPoint13(realToCanvasMat, [
1712
+ toOffset.x,
1713
+ toOffset.y
1714
+ ]);
1715
+ const [canvasToDirX, canvasToDirY] = applyToPoint13(realToCanvasMat, [
1716
+ toOffset.x + direction.x,
1717
+ toOffset.y + direction.y
1718
+ ]);
1719
+ const canvasLineAngle = Math.atan2(
1720
+ canvasToDirY - canvasToY,
1721
+ canvasToDirX - canvasToX
1722
+ );
1718
1723
  drawArrow({
1719
1724
  ctx,
1720
1725
  x: canvasFromX,
1721
1726
  y: canvasFromY,
1722
- angle: lineAngle + Math.PI,
1723
- arrowSize: scaledArrowSize,
1724
- color,
1725
- strokeWidth: scaledStrokeWidth
1727
+ angle: canvasLineAngle + Math.PI,
1728
+ arrowSize: arrowSize * scaleValue,
1729
+ color: lineColor,
1730
+ strokeWidth: strokeWidth * scaleValue
1726
1731
  });
1727
1732
  drawArrow({
1728
1733
  ctx,
1729
1734
  x: canvasToX,
1730
1735
  y: canvasToY,
1731
- angle: lineAngle,
1732
- arrowSize: scaledArrowSize,
1733
- color,
1734
- strokeWidth: scaledStrokeWidth
1736
+ angle: canvasLineAngle,
1737
+ arrowSize: arrowSize * scaleValue,
1738
+ color: lineColor,
1739
+ strokeWidth: strokeWidth * scaleValue
1735
1740
  });
1736
- if (pcbNoteDimension.text) {
1737
- let textX = (fromX + toX) / 2;
1738
- let textY = (fromY + toY) / 2;
1739
- const perpX = toY - fromY;
1740
- const perpY = -(toX - fromX);
1741
- const perpLength = Math.sqrt(perpX * perpX + perpY * perpY);
1742
- if (perpLength > 0) {
1743
- const offsetDistance = pcbNoteDimension.font_size * 1.5;
1744
- const normalizedPerpX = perpX / perpLength;
1745
- const normalizedPerpY = perpY / perpLength;
1746
- textX += normalizedPerpX * offsetDistance;
1747
- textY += normalizedPerpY * offsetDistance;
1741
+ if (text) {
1742
+ const midPoint = {
1743
+ x: (from.x + to.x) / 2 + offsetVector.x,
1744
+ y: (from.y + to.y) / 2 + offsetVector.y
1745
+ };
1746
+ const [screenFromX, screenFromY] = applyToPoint13(realToCanvasMat, [
1747
+ fromOffset.x,
1748
+ fromOffset.y
1749
+ ]);
1750
+ const [screenToX, screenToY] = applyToPoint13(realToCanvasMat, [
1751
+ toOffset.x,
1752
+ toOffset.y
1753
+ ]);
1754
+ const screenDirection = normalize({
1755
+ x: screenToX - screenFromX,
1756
+ y: screenToY - screenFromY
1757
+ });
1758
+ let textAngle = Math.atan2(screenDirection.y, screenDirection.x) * 180 / Math.PI;
1759
+ if (textAngle > 90 || textAngle < -90) {
1760
+ textAngle += 180;
1761
+ }
1762
+ const finalTextAngle = typeof textRotation === "number" && Number.isFinite(textRotation) ? textAngle - textRotation : textAngle;
1763
+ let additionalOffset = 0;
1764
+ if (text && typeof textRotation === "number" && Number.isFinite(textRotation)) {
1765
+ const textWidth = text.length * fontSize * CHARACTER_WIDTH_MULTIPLIER;
1766
+ const textHeight = fontSize;
1767
+ const rotationRad = textRotation * Math.PI / 180;
1768
+ const sinRot = Math.abs(Math.sin(rotationRad));
1769
+ const cosRot = Math.abs(Math.cos(rotationRad));
1770
+ const halfWidth = textWidth / 2;
1771
+ const halfHeight = textHeight / 2;
1772
+ const maxExtension = halfWidth * sinRot + halfHeight * cosRot;
1773
+ additionalOffset = maxExtension + fontSize * TEXT_INTERSECTION_PADDING_MULTIPLIER;
1748
1774
  }
1749
- const textRotation = -(() => {
1750
- const raw = pcbNoteDimension.text_ccw_rotation ?? lineAngle * 180 / Math.PI;
1751
- if (pcbNoteDimension.text_ccw_rotation !== void 0) return raw;
1752
- let deg = (raw + 180) % 360 - 180;
1753
- if (deg > 90) deg -= 180;
1754
- if (deg < -90) deg += 180;
1755
- return deg;
1756
- })();
1775
+ const textOffset = arrowSize * TEXT_OFFSET_MULTIPLIER + additionalOffset;
1776
+ const textPoint = {
1777
+ x: midPoint.x + perpendicular.x * textOffset,
1778
+ y: midPoint.y + perpendicular.y * textOffset
1779
+ };
1757
1780
  drawText({
1758
1781
  ctx,
1759
- text: pcbNoteDimension.text,
1760
- x: textX,
1761
- y: textY,
1762
- fontSize: pcbNoteDimension.font_size,
1763
- color,
1782
+ text,
1783
+ x: textPoint.x,
1784
+ y: textPoint.y,
1785
+ fontSize,
1786
+ color: lineColor,
1764
1787
  realToCanvasMat,
1765
1788
  anchorAlignment: "center",
1766
- rotation: textRotation
1789
+ rotation: -finalTextAngle
1790
+ // drawText expects CCW rotation in degrees
1767
1791
  });
1768
1792
  }
1769
1793
  }
1770
1794
 
1795
+ // lib/drawer/elements/pcb-note-dimension.ts
1796
+ var DEFAULT_NOTE_COLOR = "rgba(255,255,255,0.5)";
1797
+ function drawPcbNoteDimension(params) {
1798
+ const { ctx, pcbNoteDimension, realToCanvasMat } = params;
1799
+ const color = pcbNoteDimension.color ?? DEFAULT_NOTE_COLOR;
1800
+ drawDimensionLine({
1801
+ ctx,
1802
+ from: pcbNoteDimension.from,
1803
+ to: pcbNoteDimension.to,
1804
+ realToCanvasMat,
1805
+ color,
1806
+ fontSize: pcbNoteDimension.font_size,
1807
+ arrowSize: pcbNoteDimension.arrow_size,
1808
+ text: pcbNoteDimension.text,
1809
+ textRotation: pcbNoteDimension.text_ccw_rotation,
1810
+ offset: pcbNoteDimension.offset_distance && pcbNoteDimension.offset_direction ? {
1811
+ distance: pcbNoteDimension.offset_distance,
1812
+ direction: pcbNoteDimension.offset_direction
1813
+ } : void 0
1814
+ });
1815
+ }
1816
+
1817
+ // lib/drawer/elements/pcb-fabrication-note-dimension.ts
1818
+ var DEFAULT_FABRICATION_NOTE_COLOR2 = "rgba(255,255,255,0.5)";
1819
+ function drawPcbFabricationNoteDimension(params) {
1820
+ const { ctx, pcbFabricationNoteDimension, realToCanvasMat } = params;
1821
+ const color = pcbFabricationNoteDimension.color ?? DEFAULT_FABRICATION_NOTE_COLOR2;
1822
+ drawDimensionLine({
1823
+ ctx,
1824
+ from: pcbFabricationNoteDimension.from,
1825
+ to: pcbFabricationNoteDimension.to,
1826
+ realToCanvasMat,
1827
+ color,
1828
+ fontSize: pcbFabricationNoteDimension.font_size ?? 1,
1829
+ arrowSize: pcbFabricationNoteDimension.arrow_size ?? 1,
1830
+ text: pcbFabricationNoteDimension.text,
1831
+ textRotation: pcbFabricationNoteDimension.text_ccw_rotation,
1832
+ offset: pcbFabricationNoteDimension.offset_distance && pcbFabricationNoteDimension.offset_direction ? {
1833
+ distance: pcbFabricationNoteDimension.offset_distance,
1834
+ direction: pcbFabricationNoteDimension.offset_direction
1835
+ } : void 0
1836
+ });
1837
+ }
1838
+
1771
1839
  // lib/drawer/elements/pcb-note-line.ts
1772
1840
  import { applyToPoint as applyToPoint14 } from "transformation-matrix";
1773
1841
  function drawPcbNoteLine(params) {
@@ -2127,6 +2195,14 @@ var CircuitToCanvasDrawer = class {
2127
2195
  colorMap: this.colorMap
2128
2196
  });
2129
2197
  }
2198
+ if (element.type === "pcb_fabrication_note_dimension") {
2199
+ drawPcbFabricationNoteDimension({
2200
+ ctx: this.ctx,
2201
+ pcbFabricationNoteDimension: element,
2202
+ realToCanvasMat: this.realToCanvasMat,
2203
+ colorMap: this.colorMap
2204
+ });
2205
+ }
2130
2206
  }
2131
2207
  };
2132
2208
  export {
@@ -2134,6 +2210,7 @@ export {
2134
2210
  DEFAULT_PCB_COLOR_MAP,
2135
2211
  drawArrow,
2136
2212
  drawCircle,
2213
+ drawDimensionLine,
2137
2214
  drawLine,
2138
2215
  drawOval,
2139
2216
  drawPath,
@@ -2141,6 +2218,7 @@ export {
2141
2218
  drawPcbCopperPour,
2142
2219
  drawPcbCopperText,
2143
2220
  drawPcbCutout,
2221
+ drawPcbFabricationNoteDimension,
2144
2222
  drawPcbFabricationNotePath,
2145
2223
  drawPcbFabricationNoteRect,
2146
2224
  drawPcbFabricationNoteText,
@@ -23,6 +23,7 @@ import type {
23
23
  PcbNotePath,
24
24
  PcbNoteText,
25
25
  PcbNoteDimension,
26
+ PcbFabricationNoteDimension,
26
27
  PcbNoteLine,
27
28
  PcbRenderLayer,
28
29
  } from "circuit-json"
@@ -67,6 +68,7 @@ import { drawPcbFabricationNotePath } from "./elements/pcb-fabrication-note-path
67
68
  import { drawPcbNotePath } from "./elements/pcb-note-path"
68
69
  import { drawPcbNoteText } from "./elements/pcb-note-text"
69
70
  import { drawPcbNoteDimension } from "./elements/pcb-note-dimension"
71
+ import { drawPcbFabricationNoteDimension } from "./elements/pcb-fabrication-note-dimension"
70
72
  import { drawPcbNoteLine } from "./elements/pcb-note-line"
71
73
 
72
74
  export interface DrawElementsOptions {
@@ -482,5 +484,14 @@ export class CircuitToCanvasDrawer {
482
484
  colorMap: this.colorMap,
483
485
  })
484
486
  }
487
+
488
+ if (element.type === "pcb_fabrication_note_dimension") {
489
+ drawPcbFabricationNoteDimension({
490
+ ctx: this.ctx,
491
+ pcbFabricationNoteDimension: element as PcbFabricationNoteDimension,
492
+ realToCanvasMat: this.realToCanvasMat,
493
+ colorMap: this.colorMap,
494
+ })
495
+ }
485
496
  }
486
497
  }
@@ -99,3 +99,8 @@ export {
99
99
  drawPcbNoteDimension,
100
100
  type DrawPcbNoteDimensionParams,
101
101
  } from "./pcb-note-dimension"
102
+
103
+ export {
104
+ drawPcbFabricationNoteDimension,
105
+ type DrawPcbFabricationNoteDimensionParams,
106
+ } from "./pcb-fabrication-note-dimension"
@@ -0,0 +1,42 @@
1
+ import type { PcbFabricationNoteDimension } from "circuit-json"
2
+ import type { Matrix } from "transformation-matrix"
3
+ import type { PcbColorMap, CanvasContext } from "../types"
4
+ import { drawDimensionLine } from "../shapes/dimension-line"
5
+
6
+ export interface DrawPcbFabricationNoteDimensionParams {
7
+ ctx: CanvasContext
8
+ pcbFabricationNoteDimension: PcbFabricationNoteDimension
9
+ realToCanvasMat: Matrix
10
+ colorMap: PcbColorMap
11
+ }
12
+
13
+ const DEFAULT_FABRICATION_NOTE_COLOR = "rgba(255,255,255,0.5)"
14
+
15
+ export function drawPcbFabricationNoteDimension(
16
+ params: DrawPcbFabricationNoteDimensionParams,
17
+ ): void {
18
+ const { ctx, pcbFabricationNoteDimension, realToCanvasMat } = params
19
+
20
+ const color =
21
+ pcbFabricationNoteDimension.color ?? DEFAULT_FABRICATION_NOTE_COLOR
22
+
23
+ drawDimensionLine({
24
+ ctx,
25
+ from: pcbFabricationNoteDimension.from,
26
+ to: pcbFabricationNoteDimension.to,
27
+ realToCanvasMat,
28
+ color,
29
+ fontSize: pcbFabricationNoteDimension.font_size ?? 1,
30
+ arrowSize: pcbFabricationNoteDimension.arrow_size ?? 1,
31
+ text: pcbFabricationNoteDimension.text,
32
+ textRotation: pcbFabricationNoteDimension.text_ccw_rotation,
33
+ offset:
34
+ pcbFabricationNoteDimension.offset_distance &&
35
+ pcbFabricationNoteDimension.offset_direction
36
+ ? {
37
+ distance: pcbFabricationNoteDimension.offset_distance,
38
+ direction: pcbFabricationNoteDimension.offset_direction,
39
+ }
40
+ : undefined,
41
+ })
42
+ }
@@ -1,10 +1,7 @@
1
1
  import type { PcbNoteDimension } from "circuit-json"
2
2
  import type { Matrix } from "transformation-matrix"
3
- import { applyToPoint } from "transformation-matrix"
4
3
  import type { PcbColorMap, CanvasContext } from "../types"
5
- import { drawLine } from "../shapes/line"
6
- import { drawText } from "../shapes/text"
7
- import { drawArrow } from "../shapes/arrow"
4
+ import { drawDimensionLine } from "../shapes/dimension-line"
8
5
 
9
6
  export interface DrawPcbNoteDimensionParams {
10
7
  ctx: CanvasContext
@@ -19,183 +16,23 @@ export function drawPcbNoteDimension(params: DrawPcbNoteDimensionParams): void {
19
16
  const { ctx, pcbNoteDimension, realToCanvasMat } = params
20
17
 
21
18
  const color = pcbNoteDimension.color ?? DEFAULT_NOTE_COLOR
22
- const arrowSize = pcbNoteDimension.arrow_size
23
19
 
24
- // Store real (model) endpoints for extension lines
25
- const realFromX = pcbNoteDimension.from.x
26
- const realFromY = pcbNoteDimension.from.y
27
- const realToX = pcbNoteDimension.to.x
28
- const realToY = pcbNoteDimension.to.y
29
-
30
- // Calculate the dimension line endpoints (real/model coords)
31
- let fromX = realFromX
32
- let fromY = realFromY
33
- let toX = realToX
34
- let toY = realToY
35
-
36
- // Track if we have an offset (for drawing extension lines)
37
- let hasOffset = false
38
- let offsetX = 0
39
- let offsetY = 0
40
-
41
- // Apply offset if provided
42
- if (pcbNoteDimension.offset_distance && pcbNoteDimension.offset_direction) {
43
- const dirX = pcbNoteDimension.offset_direction.x
44
- const dirY = pcbNoteDimension.offset_direction.y
45
- const length = Math.hypot(dirX, dirY)
46
- if (length > 0) {
47
- const normX = dirX / length
48
- const normY = dirY / length
49
- hasOffset = true
50
- offsetX = pcbNoteDimension.offset_distance * normX
51
- offsetY = pcbNoteDimension.offset_distance * normY
52
- fromX += offsetX
53
- fromY += offsetY
54
- toX += offsetX
55
- toY += offsetY
56
- }
57
- }
58
-
59
- // Calculate stroke width to match text stroke width
60
- // Text uses fontSize * STROKE_WIDTH_RATIO (0.13) with minimum 0.35
61
- const STROKE_WIDTH_RATIO = 0.13
62
-
63
- const strokeWidth = Math.max(
64
- pcbNoteDimension.font_size * STROKE_WIDTH_RATIO,
65
- 0.35,
66
- )
67
-
68
- // Draw extension lines if offset is provided
69
- if (hasOffset) {
70
- // Extension line from original 'from' point to offset 'from' point
71
- drawLine({
72
- ctx,
73
- start: { x: realFromX, y: realFromY },
74
- end: { x: fromX, y: fromY },
75
- strokeWidth,
76
- stroke: color,
77
- realToCanvasMat: realToCanvasMat,
78
- })
79
-
80
- // Extension line from original 'to' point to offset 'to' point
81
- drawLine({
82
- ctx,
83
- start: { x: realToX, y: realToY },
84
- end: { x: toX, y: toY },
85
- strokeWidth,
86
- stroke: color,
87
- realToCanvasMat: realToCanvasMat,
88
- })
89
- }
90
-
91
- // Draw the dimension line
92
- drawLine({
93
- ctx,
94
- start: { x: fromX, y: fromY },
95
- end: { x: toX, y: toY },
96
- strokeWidth,
97
- stroke: color,
98
- realToCanvasMat: realToCanvasMat,
99
- })
100
-
101
- // Draw arrows at both ends
102
- const [canvasFromX, canvasFromY] = applyToPoint(realToCanvasMat, [
103
- fromX,
104
- fromY,
105
- ])
106
- const [canvasToX, canvasToY] = applyToPoint(realToCanvasMat, [toX, toY])
107
- // Calculate angle for arrows in canvas coordinates
108
- const canvasDx = canvasToX - canvasFromX
109
- const canvasDy = canvasToY - canvasFromY
110
- const lineAngle = Math.atan2(canvasDy, canvasDx)
111
- const scale = Math.abs(realToCanvasMat.a)
112
- const scaledArrowSize = arrowSize * scale
113
- const scaledStrokeWidth = strokeWidth * scale
114
-
115
- // Arrow at 'from' point (pointing outward, away from the line center)
116
- // This means pointing in the direction opposite to 'to'
117
- drawArrow({
20
+ drawDimensionLine({
118
21
  ctx,
119
- x: canvasFromX,
120
- y: canvasFromY,
121
- angle: lineAngle + Math.PI,
122
- arrowSize: scaledArrowSize,
22
+ from: pcbNoteDimension.from,
23
+ to: pcbNoteDimension.to,
24
+ realToCanvasMat,
123
25
  color,
124
- strokeWidth: scaledStrokeWidth,
26
+ fontSize: pcbNoteDimension.font_size,
27
+ arrowSize: pcbNoteDimension.arrow_size,
28
+ text: pcbNoteDimension.text,
29
+ textRotation: pcbNoteDimension.text_ccw_rotation,
30
+ offset:
31
+ pcbNoteDimension.offset_distance && pcbNoteDimension.offset_direction
32
+ ? {
33
+ distance: pcbNoteDimension.offset_distance,
34
+ direction: pcbNoteDimension.offset_direction,
35
+ }
36
+ : undefined,
125
37
  })
126
-
127
- // Arrow at 'to' point (pointing outward, away from the line center)
128
- // This means pointing in the direction toward 'to' (away from 'from')
129
- drawArrow({
130
- ctx,
131
- x: canvasToX,
132
- y: canvasToY,
133
- angle: lineAngle,
134
- arrowSize: scaledArrowSize,
135
- color,
136
- strokeWidth: scaledStrokeWidth,
137
- })
138
-
139
- // Draw text if provided
140
- if (pcbNoteDimension.text) {
141
- // Calculate text position (midpoint of the dimension line)
142
- // The line endpoints are already offset if offset was provided
143
- let textX = (fromX + toX) / 2
144
- let textY = (fromY + toY) / 2
145
-
146
- // Offset text perpendicular to the dimension line so it appears above/outside
147
- // Calculate perpendicular vector (rotate line direction by 90 degrees CW)
148
- // For a line from (fromX, fromY) to (toX, toY), perpendicular is (dy, -dx)
149
- // This ensures text appears above horizontal lines and to the right of vertical lines
150
- const perpX = toY - fromY
151
- const perpY = -(toX - fromX)
152
- const perpLength = Math.sqrt(perpX * perpX + perpY * perpY)
153
-
154
- // Normalize and offset by font size (plus a small gap)
155
- if (perpLength > 0) {
156
- const offsetDistance = pcbNoteDimension.font_size * 1.5 // Offset by 1.5x font size
157
- const normalizedPerpX = perpX / perpLength
158
- const normalizedPerpY = perpY / perpLength
159
- textX += normalizedPerpX * offsetDistance
160
- textY += normalizedPerpY * offsetDistance
161
- }
162
-
163
- // Calculate rotation (displayed CCW degrees). If the caller provided
164
- // `text_ccw_rotation` use that directly; otherwise align with the line
165
- // angle and keep the text upright by folding into [-90, 90]. `drawText`
166
- // expects a rotation value that it will negate internally, so we pass
167
- // `-deg` below.
168
- // Compute the displayed CCW degrees. Use the explicit `text_ccw_rotation`
169
- // when provided; otherwise derive from the line angle and fold into
170
- // [-90, 90] so text stays upright. Finally, `drawText` negates the
171
- // provided rotation when applying it to the canvas, so pass the
172
- // negative of the displayed CCW degrees.
173
- const textRotation = -(() => {
174
- const raw =
175
- pcbNoteDimension.text_ccw_rotation ?? (lineAngle * 180) / Math.PI
176
-
177
- if (pcbNoteDimension.text_ccw_rotation !== undefined) return raw
178
-
179
- // Normalize to [-180, 180]
180
- let deg = ((raw + 180) % 360) - 180
181
-
182
- // Fold into [-90, 90]
183
- if (deg > 90) deg -= 180
184
- if (deg < -90) deg += 180
185
-
186
- return deg
187
- })()
188
-
189
- drawText({
190
- ctx,
191
- text: pcbNoteDimension.text,
192
- x: textX,
193
- y: textY,
194
- fontSize: pcbNoteDimension.font_size,
195
- color,
196
- realToCanvasMat: realToCanvasMat,
197
- anchorAlignment: "center",
198
- rotation: textRotation,
199
- })
200
- }
201
38
  }
@@ -0,0 +1,222 @@
1
+ import type { Matrix } from "transformation-matrix"
2
+ import { applyToPoint } from "transformation-matrix"
3
+ import type { CanvasContext } from "../types"
4
+ import { drawLine } from "./line"
5
+ import { drawText } from "./text"
6
+ import { drawArrow } from "./arrow"
7
+
8
+ export interface DrawDimensionLineParams {
9
+ ctx: CanvasContext
10
+ from: { x: number; y: number }
11
+ to: { x: number; y: number }
12
+ realToCanvasMat: Matrix
13
+ color: string
14
+ fontSize: number
15
+ arrowSize?: number
16
+ strokeWidth?: number
17
+ text?: string
18
+ textRotation?: number // CCW rotation in degrees
19
+ offset?: {
20
+ distance: number
21
+ direction: { x: number; y: number }
22
+ }
23
+ }
24
+
25
+ const TEXT_OFFSET_MULTIPLIER = 1.5
26
+ const CHARACTER_WIDTH_MULTIPLIER = 0.6
27
+ const TEXT_INTERSECTION_PADDING_MULTIPLIER = 0.3
28
+
29
+ function normalize(v: { x: number; y: number }) {
30
+ const len = Math.hypot(v.x, v.y) || 1
31
+ return { x: v.x / len, y: v.y / len }
32
+ }
33
+
34
+ export function drawDimensionLine(params: DrawDimensionLineParams): void {
35
+ const {
36
+ ctx,
37
+ from,
38
+ to,
39
+ realToCanvasMat,
40
+ color,
41
+ fontSize,
42
+ arrowSize = 1,
43
+ strokeWidth: manualStrokeWidth,
44
+ text,
45
+ textRotation,
46
+ offset,
47
+ } = params
48
+
49
+ const direction = normalize({ x: to.x - from.x, y: to.y - from.y })
50
+ const perpendicular = { x: -direction.y, y: direction.x }
51
+
52
+ const hasOffsetDirection =
53
+ offset?.direction &&
54
+ typeof offset.direction.x === "number" &&
55
+ typeof offset.direction.y === "number"
56
+
57
+ const normalizedOffsetDirection = hasOffsetDirection
58
+ ? normalize(offset!.direction)
59
+ : { x: 0, y: 0 }
60
+
61
+ const offsetMagnitude = offset?.distance ?? 0
62
+ const offsetVector = {
63
+ x: normalizedOffsetDirection.x * offsetMagnitude,
64
+ y: normalizedOffsetDirection.y * offsetMagnitude,
65
+ }
66
+
67
+ const fromOffset = { x: from.x + offsetVector.x, y: from.y + offsetVector.y }
68
+ const toOffset = { x: to.x + offsetVector.x, y: to.y + offsetVector.y }
69
+
70
+ const fromBase = fromOffset
71
+ const toBase = toOffset
72
+
73
+ const scaleValue = Math.abs(realToCanvasMat.a)
74
+ const strokeWidth = manualStrokeWidth ?? arrowSize / 5
75
+ const lineColor = color || "rgba(255,255,255,0.5)"
76
+
77
+ // Extension lines (ticks)
78
+ const extensionDirection =
79
+ hasOffsetDirection &&
80
+ (Math.abs(normalizedOffsetDirection.x) > Number.EPSILON ||
81
+ Math.abs(normalizedOffsetDirection.y) > Number.EPSILON)
82
+ ? normalizedOffsetDirection
83
+ : perpendicular
84
+
85
+ const extensionLength = offsetMagnitude + 0.5
86
+
87
+ const drawExtension = (anchor: { x: number; y: number }) => {
88
+ const endPoint = {
89
+ x: anchor.x + extensionDirection.x * extensionLength,
90
+ y: anchor.y + extensionDirection.y * extensionLength,
91
+ }
92
+ drawLine({
93
+ ctx,
94
+ start: anchor,
95
+ end: endPoint,
96
+ strokeWidth,
97
+ stroke: lineColor,
98
+ realToCanvasMat,
99
+ })
100
+ }
101
+
102
+ drawExtension(from)
103
+ drawExtension(to)
104
+
105
+ // Main dimension line
106
+ drawLine({
107
+ ctx,
108
+ start: fromBase,
109
+ end: toBase,
110
+ strokeWidth,
111
+ stroke: lineColor,
112
+ realToCanvasMat,
113
+ })
114
+
115
+ // Arrows (Keep V-shaped but matching size)
116
+ const [canvasFromX, canvasFromY] = applyToPoint(realToCanvasMat, [
117
+ fromOffset.x,
118
+ fromOffset.y,
119
+ ])
120
+ const [canvasToX, canvasToY] = applyToPoint(realToCanvasMat, [
121
+ toOffset.x,
122
+ toOffset.y,
123
+ ])
124
+ const [canvasToDirX, canvasToDirY] = applyToPoint(realToCanvasMat, [
125
+ toOffset.x + direction.x,
126
+ toOffset.y + direction.y,
127
+ ])
128
+
129
+ const canvasLineAngle = Math.atan2(
130
+ canvasToDirY - canvasToY,
131
+ canvasToDirX - canvasToX,
132
+ )
133
+
134
+ drawArrow({
135
+ ctx,
136
+ x: canvasFromX,
137
+ y: canvasFromY,
138
+ angle: canvasLineAngle + Math.PI,
139
+ arrowSize: arrowSize * scaleValue,
140
+ color: lineColor,
141
+ strokeWidth: strokeWidth * scaleValue,
142
+ })
143
+
144
+ drawArrow({
145
+ ctx,
146
+ x: canvasToX,
147
+ y: canvasToY,
148
+ angle: canvasLineAngle,
149
+ arrowSize: arrowSize * scaleValue,
150
+ color: lineColor,
151
+ strokeWidth: strokeWidth * scaleValue,
152
+ })
153
+
154
+ // Text
155
+ if (text) {
156
+ const midPoint = {
157
+ x: (from.x + to.x) / 2 + offsetVector.x,
158
+ y: (from.y + to.y) / 2 + offsetVector.y,
159
+ }
160
+
161
+ const [screenFromX, screenFromY] = applyToPoint(realToCanvasMat, [
162
+ fromOffset.x,
163
+ fromOffset.y,
164
+ ])
165
+ const [screenToX, screenToY] = applyToPoint(realToCanvasMat, [
166
+ toOffset.x,
167
+ toOffset.y,
168
+ ])
169
+
170
+ const screenDirection = normalize({
171
+ x: screenToX - screenFromX,
172
+ y: screenToY - screenFromY,
173
+ })
174
+
175
+ let textAngle =
176
+ (Math.atan2(screenDirection.y, screenDirection.x) * 180) / Math.PI
177
+ if (textAngle > 90 || textAngle < -90) {
178
+ textAngle += 180
179
+ }
180
+
181
+ const finalTextAngle =
182
+ typeof textRotation === "number" && Number.isFinite(textRotation)
183
+ ? textAngle - textRotation
184
+ : textAngle
185
+
186
+ let additionalOffset = 0
187
+ if (
188
+ text &&
189
+ typeof textRotation === "number" &&
190
+ Number.isFinite(textRotation)
191
+ ) {
192
+ const textWidth = text.length * fontSize * CHARACTER_WIDTH_MULTIPLIER
193
+ const textHeight = fontSize
194
+ const rotationRad = (textRotation * Math.PI) / 180
195
+ const sinRot = Math.abs(Math.sin(rotationRad))
196
+ const cosRot = Math.abs(Math.cos(rotationRad))
197
+ const halfWidth = textWidth / 2
198
+ const halfHeight = textHeight / 2
199
+ const maxExtension = halfWidth * sinRot + halfHeight * cosRot
200
+ additionalOffset =
201
+ maxExtension + fontSize * TEXT_INTERSECTION_PADDING_MULTIPLIER
202
+ }
203
+
204
+ const textOffset = arrowSize * TEXT_OFFSET_MULTIPLIER + additionalOffset
205
+ const textPoint = {
206
+ x: midPoint.x + perpendicular.x * textOffset,
207
+ y: midPoint.y + perpendicular.y * textOffset,
208
+ }
209
+
210
+ drawText({
211
+ ctx,
212
+ text,
213
+ x: textPoint.x,
214
+ y: textPoint.y,
215
+ fontSize,
216
+ color: lineColor,
217
+ realToCanvasMat,
218
+ anchorAlignment: "center",
219
+ rotation: -finalTextAngle, // drawText expects CCW rotation in degrees
220
+ })
221
+ }
222
+ }
@@ -6,6 +6,10 @@ export { drawPolygon, type DrawPolygonParams } from "./polygon"
6
6
  export { drawLine, type DrawLineParams } from "./line"
7
7
  export { drawPath, type DrawPathParams } from "./path"
8
8
  export { drawArrow, type DrawArrowParams } from "./arrow"
9
+ export {
10
+ drawDimensionLine,
11
+ type DrawDimensionLineParams,
12
+ } from "./dimension-line"
9
13
  export {
10
14
  drawText,
11
15
  type DrawTextParams,
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.32",
4
+ "version": "0.0.34",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "tsup-node ./lib/index.ts --format esm --dts",
@@ -0,0 +1,40 @@
1
+ import { expect, test } from "bun:test"
2
+ import type {
3
+ AnyCircuitElement,
4
+ PcbFabricationNoteDimension,
5
+ } from "circuit-json"
6
+ import { getStackedPngSvgComparison } from "../fixtures/getStackedPngSvgComparison"
7
+
8
+ test("draw pcb fabrication note dimension - basic", async () => {
9
+ const circuitJson: AnyCircuitElement[] = [
10
+ {
11
+ type: "pcb_board",
12
+ pcb_board_id: "board1",
13
+ center: { x: 10, y: 5 },
14
+ width: 25,
15
+ height: 15,
16
+ thickness: 1.6,
17
+ num_layers: 2,
18
+ material: "fr4",
19
+ },
20
+ {
21
+ type: "pcb_fabrication_note_dimension",
22
+ pcb_fabrication_note_dimension_id: "fab_dim_1",
23
+ from: { x: 2, y: 5 },
24
+ to: { x: 18, y: 5 },
25
+ arrow_size: 0.4,
26
+ font_size: 0.6,
27
+ text: "16mm",
28
+ layer: "top",
29
+ pcb_component_id: "comp_1",
30
+ font: "tscircuit2024",
31
+ },
32
+ ]
33
+
34
+ const stackedPng = await getStackedPngSvgComparison(circuitJson, {
35
+ width: 400,
36
+ height: 800,
37
+ })
38
+
39
+ await expect(stackedPng).toMatchPngSnapshot(import.meta.path)
40
+ })
@@ -0,0 +1,46 @@
1
+ import { expect, test } from "bun:test"
2
+ import { createCanvas } from "@napi-rs/canvas"
3
+ import { identity } from "transformation-matrix"
4
+ import { drawDimensionLine } from "../../lib/drawer/shapes/dimension-line"
5
+
6
+ test("drawDimensionLine shape", async () => {
7
+ const width = 400
8
+ const height = 200
9
+ const canvas = createCanvas(width, height)
10
+ const ctx = canvas.getContext("2d")
11
+
12
+ ctx.fillStyle = "#1a1a1a"
13
+ ctx.fillRect(0, 0, width, height)
14
+
15
+ // Basic horizontal dimension
16
+ drawDimensionLine({
17
+ ctx,
18
+ from: { x: 50, y: 50 },
19
+ to: { x: 350, y: 50 },
20
+ realToCanvasMat: identity(),
21
+ color: "white",
22
+ fontSize: 12,
23
+ arrowSize: 8,
24
+ text: "300mm",
25
+ })
26
+
27
+ // Vertical dimension with offset and extension lines
28
+ drawDimensionLine({
29
+ ctx,
30
+ from: { x: 50, y: 80 },
31
+ to: { x: 50, y: 180 },
32
+ realToCanvasMat: identity(),
33
+ color: "cyan",
34
+ fontSize: 10,
35
+ arrowSize: 6,
36
+ text: "100mm",
37
+ offset: {
38
+ distance: 30,
39
+ direction: { x: 1, y: 0 },
40
+ },
41
+ })
42
+
43
+ await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
44
+ import.meta.path,
45
+ )
46
+ })