circuit-to-canvas 0.0.2 → 0.0.4

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 (37) hide show
  1. package/README.md +64 -0
  2. package/dist/index.d.ts +74 -3
  3. package/dist/index.js +326 -4
  4. package/lib/drawer/CircuitToCanvasDrawer.ts +44 -0
  5. package/lib/drawer/elements/index.ts +20 -0
  6. package/lib/drawer/elements/pcb-copper-text.ts +99 -0
  7. package/lib/drawer/elements/pcb-fabrication-note-path.ts +41 -0
  8. package/lib/drawer/elements/pcb-fabrication-note-rect.ts +39 -0
  9. package/lib/drawer/elements/pcb-fabrication-note-text.ts +42 -0
  10. package/lib/drawer/shapes/index.ts +9 -0
  11. package/lib/drawer/shapes/rect.ts +28 -3
  12. package/lib/drawer/shapes/text.ts +218 -0
  13. package/lib/drawer/types.ts +8 -0
  14. package/package.json +3 -1
  15. package/tests/elements/__snapshots__/pcb-copper-text-knockout.snap.png +0 -0
  16. package/tests/elements/__snapshots__/pcb-copper-text.snap.png +0 -0
  17. package/tests/elements/__snapshots__/pcb-fabrication-note-path-custom-color.snap.png +0 -0
  18. package/tests/elements/__snapshots__/pcb-fabrication-note-path-thick-stroke.snap.png +0 -0
  19. package/tests/elements/__snapshots__/pcb-fabrication-note-path.snap.png +0 -0
  20. package/tests/elements/__snapshots__/pcb-fabrication-note-rect-all-features.snap.png +0 -0
  21. package/tests/elements/__snapshots__/pcb-fabrication-note-rect-corner-radius.snap.png +0 -0
  22. package/tests/elements/__snapshots__/pcb-fabrication-note-rect-custom-color.snap.png +0 -0
  23. package/tests/elements/__snapshots__/pcb-fabrication-note-rect-dashed.snap.png +0 -0
  24. package/tests/elements/__snapshots__/pcb-fabrication-note-rect-default-color.snap.png +0 -0
  25. package/tests/elements/__snapshots__/pcb-fabrication-note-text-rgba-color.snap.png +0 -0
  26. package/tests/elements/__snapshots__/pcb-fabrication-note-text-small.snap.png +0 -0
  27. package/tests/elements/pcb-copper-text.test.ts +102 -0
  28. package/tests/elements/pcb-fabrication-note-path-custom-color.test.ts +37 -0
  29. package/tests/elements/pcb-fabrication-note-path-thick-stroke.test.ts +37 -0
  30. package/tests/elements/pcb-fabrication-note-path.test.ts +36 -0
  31. package/tests/elements/pcb-fabrication-note-rect-all-features.test.ts +35 -0
  32. package/tests/elements/pcb-fabrication-note-rect-corner-radius.test.ts +33 -0
  33. package/tests/elements/pcb-fabrication-note-rect-custom-color.test.ts +32 -0
  34. package/tests/elements/pcb-fabrication-note-rect-dashed.test.ts +33 -0
  35. package/tests/elements/pcb-fabrication-note-rect-default-color.test.ts +32 -0
  36. package/tests/elements/pcb-fabrication-note-text-rgba-color.test.ts +34 -0
  37. package/tests/elements/pcb-fabrication-note-text-small.test.ts +33 -0
package/README.md CHANGED
@@ -30,3 +30,67 @@ There are two "types" of layers:
30
30
  - Layer groups "top" (includes "top_copper", "top_soldermask")
31
31
 
32
32
  inner layers go by the name inner1, inner2 etc.
33
+
34
+ ## Feature Parity with circuit-to-svg
35
+
36
+ This checklist tracks PCB drawing features from [circuit-to-svg](https://github.com/tscircuit/circuit-to-svg) that are implemented in circuit-to-canvas.
37
+
38
+ ### PCB Elements
39
+
40
+ - [x] `pcb_board` - Board outline with center/width/height or custom outline
41
+ - [x] `pcb_trace` - PCB traces with route points
42
+ - [x] `pcb_via` - Via holes
43
+ - [x] `pcb_plated_hole` - Plated through-holes (circular, pill, oval shapes)
44
+ - [x] `pcb_hole` - Non-plated holes (circular, square, oval shapes)
45
+ - [x] `pcb_smtpad` - SMT pads (rect, circle, rotated_rect, pill shapes)
46
+ - [x] `pcb_copper_pour` - Copper pour areas (rect, polygon shapes)
47
+ - [x] `pcb_cutout` - Board cutouts (rect, circle, polygon shapes)
48
+
49
+ ### Silkscreen Elements
50
+
51
+ - [x] `pcb_silkscreen_text` - Text on silkscreen layer
52
+ - [x] `pcb_silkscreen_rect` - Rectangles on silkscreen
53
+ - [x] `pcb_silkscreen_circle` - Circles on silkscreen
54
+ - [x] `pcb_silkscreen_line` - Lines on silkscreen
55
+ - [x] `pcb_silkscreen_path` - Paths/routes on silkscreen
56
+
57
+ ### Copper Text
58
+
59
+ - [ ] `pcb_copper_text` - Text rendered on copper layers (supports knockout mode, mirroring)
60
+
61
+ ### Error Visualization
62
+
63
+ - [ ] `pcb_trace_error` - Trace routing error indicators
64
+ - [ ] `pcb_footprint_overlap_error` - Footprint overlap error indicators
65
+
66
+ ### Debug/Development Features
67
+
68
+ - [ ] `pcb_component` - Component bounding box visualization
69
+ - [ ] `pcb_group` - PCB group visualization with dashed outlines
70
+ - [ ] `pcb_courtyard_rect` - Component courtyard rectangles
71
+
72
+ ### Fabrication Notes
73
+
74
+ - [x] `pcb_fabrication_note_text` - Fabrication note text
75
+ - [ ] `pcb_fabrication_note_rect` - Fabrication note rectangles
76
+ - [ ] `pcb_fabrication_note_path` - Fabrication note paths
77
+ - [ ] `pcb_fabrication_note_dimension` - Fabrication dimension annotations
78
+
79
+ ### Annotation/Notes
80
+
81
+ - [ ] `pcb_note_text` - General note text
82
+ - [ ] `pcb_note_rect` - Note rectangles
83
+ - [ ] `pcb_note_path` - Note paths
84
+ - [ ] `pcb_note_line` - Note lines
85
+ - [ ] `pcb_note_dimension` - Dimension annotations
86
+
87
+ ### Panel Support
88
+
89
+ - [ ] `pcb_panel` - PCB panel outlines for panelization
90
+
91
+ ### Visualization Features
92
+
93
+ - [ ] Rats nest visualization - Unrouted connection indicators
94
+ - [ ] PCB grid overlay - Configurable grid with major/minor lines
95
+ - [ ] Soldermask rendering - Soldermask layer visualization
96
+ - [ ] Anchor offset indicators - Debug indicators for relative positioning
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AnyCircuitElement, PcbPlatedHole, PCBVia, PCBHole, PcbSmtPad, PCBTrace, PcbBoard, PcbSilkscreenText, PcbSilkscreenRect, PcbSilkscreenCircle, PcbSilkscreenLine, PcbSilkscreenPath, PcbCutout, PcbCopperPour } from 'circuit-json';
1
+ import { AnyCircuitElement, PcbPlatedHole, PCBVia, PCBHole, PcbSmtPad, PCBTrace, PcbBoard, PcbSilkscreenText, PcbSilkscreenRect, PcbSilkscreenCircle, PcbSilkscreenLine, PcbSilkscreenPath, PcbCutout, PcbCopperPour, PcbCopperText, PcbFabricationNoteText, PcbFabricationNoteRect, PcbFabricationNotePath } from 'circuit-json';
2
2
  import { Matrix } from 'transformation-matrix';
3
3
 
4
4
  /**
@@ -21,16 +21,24 @@ interface CanvasContext {
21
21
  translate(x: number, y: number): void;
22
22
  rotate(angle: number): void;
23
23
  scale(x: number, y: number): void;
24
+ globalCompositeOperation?: string;
24
25
  fillStyle: string | CanvasGradient | CanvasPattern;
25
26
  strokeStyle: string | CanvasGradient | CanvasPattern;
26
27
  lineWidth: number;
27
28
  lineCap: "butt" | "round" | "square";
28
29
  lineJoin: "bevel" | "round" | "miter";
30
+ setLineDash(segments: number[]): void;
29
31
  canvas: {
30
32
  width: number;
31
33
  height: number;
32
34
  };
33
35
  fillText(text: string, x: number, y: number): void;
36
+ fillRect(x: number, y: number, width: number, height: number): void;
37
+ measureText?: (text: string) => {
38
+ width: number;
39
+ actualBoundingBoxAscent?: number;
40
+ actualBoundingBoxDescent?: number;
41
+ };
34
42
  font: string;
35
43
  textAlign: "start" | "end" | "left" | "right" | "center";
36
44
  textBaseline: "top" | "hanging" | "middle" | "alphabetic" | "ideographic" | "bottom";
@@ -115,10 +123,13 @@ interface DrawRectParams {
115
123
  };
116
124
  width: number;
117
125
  height: number;
118
- fill: string;
126
+ fill?: string;
119
127
  transform: Matrix;
120
128
  borderRadius?: number;
121
129
  rotation?: number;
130
+ stroke?: string;
131
+ strokeWidth?: number;
132
+ isStrokeDashed?: boolean;
122
133
  }
123
134
  declare function drawRect(params: DrawRectParams): void;
124
135
 
@@ -192,6 +203,34 @@ interface DrawPathParams {
192
203
  }
193
204
  declare function drawPath(params: DrawPathParams): void;
194
205
 
206
+ type AlphabetLayout = {
207
+ width: number;
208
+ height: number;
209
+ glyphWidth: number;
210
+ letterSpacing: number;
211
+ spaceWidth: number;
212
+ strokeWidth: number;
213
+ };
214
+ declare function getAlphabetLayout(text: string, fontSize: number): AlphabetLayout;
215
+ type AnchorAlignment = "center" | "top_left" | "top_right" | "bottom_left" | "bottom_right" | "left" | "right" | "top" | "bottom";
216
+ declare function getTextStartPosition(alignment: AnchorAlignment, layout: AlphabetLayout): {
217
+ x: number;
218
+ y: number;
219
+ };
220
+ declare function strokeAlphabetText(ctx: CanvasContext, text: string, layout: AlphabetLayout, startX: number, startY: number): void;
221
+ interface DrawTextParams {
222
+ ctx: CanvasContext;
223
+ text: string;
224
+ x: number;
225
+ y: number;
226
+ fontSize: number;
227
+ color: string;
228
+ transform: Matrix;
229
+ anchorAlignment: AnchorAlignment;
230
+ rotation?: number;
231
+ }
232
+ declare function drawText(params: DrawTextParams): void;
233
+
195
234
  interface DrawPcbPlatedHoleParams {
196
235
  ctx: CanvasContext;
197
236
  hole: PcbPlatedHole;
@@ -292,4 +331,36 @@ interface DrawPcbCopperPourParams {
292
331
  }
293
332
  declare function drawPcbCopperPour(params: DrawPcbCopperPourParams): void;
294
333
 
295
- export { type CameraBounds, type CanvasContext, CircuitToCanvasDrawer, type CopperColorMap, type CopperLayerName, DEFAULT_PCB_COLOR_MAP, type DrawCircleParams, type DrawContext, type DrawElementsOptions, type DrawLineParams, type DrawOvalParams, type DrawPathParams, type DrawPcbBoardParams, type DrawPcbCopperPourParams, type DrawPcbCutoutParams, type DrawPcbHoleParams, type DrawPcbPlatedHoleParams, type DrawPcbSilkscreenCircleParams, type DrawPcbSilkscreenLineParams, type DrawPcbSilkscreenPathParams, type DrawPcbSilkscreenRectParams, type DrawPcbSilkscreenTextParams, type DrawPcbSmtPadParams, type DrawPcbTraceParams, type DrawPcbViaParams, type DrawPillParams, type DrawPolygonParams, type DrawRectParams, type DrawerConfig, type PcbColorMap, drawCircle, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCutout, drawPcbHole, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenPath, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect };
334
+ interface DrawPcbCopperTextParams {
335
+ ctx: CanvasContext;
336
+ text: PcbCopperText;
337
+ transform: Matrix;
338
+ colorMap: PcbColorMap;
339
+ }
340
+ declare function drawPcbCopperText(params: DrawPcbCopperTextParams): void;
341
+
342
+ interface DrawPcbFabricationNoteTextParams {
343
+ ctx: CanvasContext;
344
+ text: PcbFabricationNoteText;
345
+ transform: Matrix;
346
+ colorMap: PcbColorMap;
347
+ }
348
+ declare function drawPcbFabricationNoteText(params: DrawPcbFabricationNoteTextParams): void;
349
+
350
+ interface DrawPcbFabricationNoteRectParams {
351
+ ctx: CanvasContext;
352
+ rect: PcbFabricationNoteRect;
353
+ transform: Matrix;
354
+ colorMap: PcbColorMap;
355
+ }
356
+ declare function drawPcbFabricationNoteRect(params: DrawPcbFabricationNoteRectParams): void;
357
+
358
+ interface DrawPcbFabricationNotePathParams {
359
+ ctx: CanvasContext;
360
+ path: PcbFabricationNotePath;
361
+ transform: Matrix;
362
+ colorMap: PcbColorMap;
363
+ }
364
+ declare function drawPcbFabricationNotePath(params: DrawPcbFabricationNotePathParams): void;
365
+
366
+ export { type AlphabetLayout, type AnchorAlignment, type CameraBounds, type CanvasContext, CircuitToCanvasDrawer, type CopperColorMap, type CopperLayerName, DEFAULT_PCB_COLOR_MAP, 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 DrawPcbPlatedHoleParams, type DrawPcbSilkscreenCircleParams, type DrawPcbSilkscreenLineParams, type DrawPcbSilkscreenPathParams, type DrawPcbSilkscreenRectParams, type DrawPcbSilkscreenTextParams, type DrawPcbSmtPadParams, type DrawPcbTraceParams, type DrawPcbViaParams, type DrawPillParams, type DrawPolygonParams, type DrawRectParams, type DrawTextParams, type DrawerConfig, type PcbColorMap, drawCircle, drawLine, drawOval, drawPath, drawPcbBoard, drawPcbCopperPour, drawPcbCopperText, drawPcbCutout, drawPcbFabricationNotePath, drawPcbFabricationNoteRect, drawPcbFabricationNoteText, drawPcbHole, drawPcbPlatedHole, drawPcbSilkscreenCircle, drawPcbSilkscreenLine, drawPcbSilkscreenPath, drawPcbSilkscreenRect, drawPcbSilkscreenText, drawPcbSmtPad, drawPcbTrace, drawPcbVia, drawPill, drawPolygon, drawRect, drawText, getAlphabetLayout, getTextStartPosition, strokeAlphabetText };
package/dist/index.js CHANGED
@@ -58,17 +58,26 @@ function drawRect(params) {
58
58
  fill,
59
59
  transform,
60
60
  borderRadius = 0,
61
- rotation = 0
61
+ rotation = 0,
62
+ stroke,
63
+ strokeWidth,
64
+ isStrokeDashed = false
62
65
  } = params;
63
66
  const [cx, cy] = applyToPoint2(transform, [center.x, center.y]);
64
67
  const scaledWidth = width * Math.abs(transform.a);
65
68
  const scaledHeight = height * Math.abs(transform.a);
66
69
  const scaledRadius = borderRadius * Math.abs(transform.a);
70
+ const scaledStrokeWidth = strokeWidth ? strokeWidth * Math.abs(transform.a) : void 0;
67
71
  ctx.save();
68
72
  ctx.translate(cx, cy);
69
73
  if (rotation !== 0) {
70
74
  ctx.rotate(-rotation * (Math.PI / 180));
71
75
  }
76
+ if (isStrokeDashed && scaledStrokeWidth) {
77
+ ctx.setLineDash([scaledStrokeWidth * 2, scaledStrokeWidth * 2]);
78
+ } else {
79
+ ctx.setLineDash([]);
80
+ }
72
81
  ctx.beginPath();
73
82
  if (scaledRadius > 0) {
74
83
  const x = -scaledWidth / 2;
@@ -92,8 +101,15 @@ function drawRect(params) {
92
101
  } else {
93
102
  ctx.rect(-scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight);
94
103
  }
95
- ctx.fillStyle = fill;
96
- ctx.fill();
104
+ if (fill) {
105
+ ctx.fillStyle = fill;
106
+ ctx.fill();
107
+ }
108
+ if (stroke && scaledStrokeWidth) {
109
+ ctx.strokeStyle = stroke;
110
+ ctx.lineWidth = scaledStrokeWidth;
111
+ ctx.stroke();
112
+ }
97
113
  ctx.restore();
98
114
  }
99
115
 
@@ -794,6 +810,272 @@ function drawPcbCopperPour(params) {
794
810
  ctx.restore();
795
811
  }
796
812
 
813
+ // lib/drawer/elements/pcb-copper-text.ts
814
+ import { applyToPoint as applyToPoint11 } from "transformation-matrix";
815
+
816
+ // lib/drawer/shapes/text.ts
817
+ import { lineAlphabet } from "@tscircuit/alphabet";
818
+ import { applyToPoint as applyToPoint10 } from "transformation-matrix";
819
+ var GLYPH_WIDTH_RATIO = 0.62;
820
+ var LETTER_SPACING_RATIO = 0.3;
821
+ var SPACE_WIDTH_RATIO = 1;
822
+ var STROKE_WIDTH_RATIO = 0.13;
823
+ var CURVED_GLYPHS = /* @__PURE__ */ new Set(["O", "o", "0"]);
824
+ function getAlphabetLayout(text, fontSize) {
825
+ const glyphWidth = fontSize * GLYPH_WIDTH_RATIO;
826
+ const letterSpacing = glyphWidth * LETTER_SPACING_RATIO;
827
+ const spaceWidth = glyphWidth * SPACE_WIDTH_RATIO;
828
+ const characters = Array.from(text);
829
+ let width = 0;
830
+ characters.forEach((char, index) => {
831
+ const advance = char === " " ? spaceWidth : glyphWidth;
832
+ width += advance;
833
+ if (index < characters.length - 1) width += letterSpacing;
834
+ });
835
+ const strokeWidth = Math.max(fontSize * STROKE_WIDTH_RATIO, 0.35);
836
+ return {
837
+ width,
838
+ height: fontSize,
839
+ glyphWidth,
840
+ letterSpacing,
841
+ spaceWidth,
842
+ strokeWidth
843
+ };
844
+ }
845
+ var getGlyphLines = (char) => lineAlphabet[char] ?? lineAlphabet[char.toUpperCase()];
846
+ function getTextStartPosition(alignment, layout) {
847
+ const totalWidth = layout.width + layout.strokeWidth;
848
+ const totalHeight = layout.height + layout.strokeWidth;
849
+ let x = 0;
850
+ let y = 0;
851
+ if (alignment === "center") {
852
+ x = -totalWidth / 2;
853
+ } else if (alignment === "top_left" || alignment === "bottom_left" || alignment === "left") {
854
+ x = 0;
855
+ } else if (alignment === "top_right" || alignment === "bottom_right" || alignment === "right") {
856
+ x = -totalWidth;
857
+ }
858
+ if (alignment === "center") {
859
+ y = -totalHeight / 2;
860
+ } else if (alignment === "top_left" || alignment === "top_right" || alignment === "top") {
861
+ y = 0;
862
+ } else if (alignment === "bottom_left" || alignment === "bottom_right" || alignment === "bottom") {
863
+ y = -totalHeight;
864
+ }
865
+ return { x, y };
866
+ }
867
+ function strokeAlphabetText(ctx, text, layout, startX, startY) {
868
+ const { glyphWidth, letterSpacing, spaceWidth, height, strokeWidth } = layout;
869
+ const yOffset = startY + height / 2;
870
+ const characters = Array.from(text);
871
+ let cursor = startX + strokeWidth / 2;
872
+ characters.forEach((char, index) => {
873
+ const lines = getGlyphLines(char);
874
+ const advance = char === " " ? spaceWidth : glyphWidth;
875
+ if (CURVED_GLYPHS.has(char)) {
876
+ const radiusX = Math.max(glyphWidth / 2 - strokeWidth / 2, strokeWidth);
877
+ const radiusY = Math.max(height / 2 - strokeWidth / 2, strokeWidth);
878
+ const centerY = yOffset - height / 2;
879
+ ctx.beginPath();
880
+ ctx.ellipse(
881
+ cursor + glyphWidth / 2,
882
+ centerY,
883
+ radiusX,
884
+ radiusY,
885
+ 0,
886
+ 0,
887
+ Math.PI * 2
888
+ );
889
+ ctx.stroke();
890
+ } else if (lines?.length) {
891
+ ctx.beginPath();
892
+ for (const line of lines) {
893
+ const x1 = cursor + line.x1 * glyphWidth;
894
+ const y1 = yOffset - line.y1 * height;
895
+ const x2 = cursor + line.x2 * glyphWidth;
896
+ const y2 = yOffset - line.y2 * height;
897
+ ctx.moveTo(x1, y1);
898
+ ctx.lineTo(x2, y2);
899
+ }
900
+ ctx.stroke();
901
+ }
902
+ cursor += advance;
903
+ if (index < characters.length - 1) {
904
+ cursor += letterSpacing;
905
+ }
906
+ });
907
+ }
908
+ function drawText(params) {
909
+ const {
910
+ ctx,
911
+ text,
912
+ x,
913
+ y,
914
+ fontSize,
915
+ color,
916
+ transform,
917
+ anchorAlignment,
918
+ rotation = 0
919
+ } = params;
920
+ if (!text) return;
921
+ const [transformedX, transformedY] = applyToPoint10(transform, [x, y]);
922
+ const scale2 = Math.abs(transform.a);
923
+ const scaledFontSize = fontSize * scale2;
924
+ const layout = getAlphabetLayout(text, scaledFontSize);
925
+ const startPos = getTextStartPosition(anchorAlignment, layout);
926
+ ctx.save();
927
+ ctx.translate(transformedX, transformedY);
928
+ if (rotation !== 0) {
929
+ ctx.rotate(-rotation * (Math.PI / 180));
930
+ }
931
+ ctx.lineWidth = layout.strokeWidth;
932
+ ctx.lineCap = "round";
933
+ ctx.lineJoin = "round";
934
+ ctx.strokeStyle = color;
935
+ strokeAlphabetText(
936
+ ctx,
937
+ text,
938
+ layout,
939
+ startPos.x,
940
+ startPos.y + layout.strokeWidth / 2
941
+ );
942
+ ctx.restore();
943
+ }
944
+
945
+ // lib/drawer/elements/pcb-copper-text.ts
946
+ var DEFAULT_PADDING = { left: 0.2, right: 0.2, top: 0.2, bottom: 0.2 };
947
+ function layerToCopperColor(layer, colorMap) {
948
+ return colorMap.copper[layer] ?? colorMap.copper.top;
949
+ }
950
+ function mapAnchorAlignment2(alignment) {
951
+ if (!alignment) return "center";
952
+ if (alignment.includes("left")) return "left";
953
+ if (alignment.includes("right")) return "right";
954
+ return "center";
955
+ }
956
+ function drawPcbCopperText(params) {
957
+ const { ctx, text, transform, colorMap } = params;
958
+ const content = text.text ?? "";
959
+ if (!content) return;
960
+ const [x, y] = applyToPoint11(transform, [
961
+ text.anchor_position.x,
962
+ text.anchor_position.y
963
+ ]);
964
+ const scale2 = Math.abs(transform.a);
965
+ const fontSize = (text.font_size ?? 1) * scale2;
966
+ const rotation = text.ccw_rotation ?? 0;
967
+ const padding = {
968
+ ...DEFAULT_PADDING,
969
+ ...text.knockout_padding
970
+ };
971
+ const textColor = layerToCopperColor(text.layer, colorMap);
972
+ const layout = getAlphabetLayout(content, fontSize);
973
+ const totalWidth = layout.width + layout.strokeWidth;
974
+ const totalHeight = layout.height + layout.strokeWidth;
975
+ const alignment = mapAnchorAlignment2(text.anchor_alignment);
976
+ const startPos = getTextStartPosition(alignment, layout);
977
+ const startX = startPos.x;
978
+ const startY = 0;
979
+ ctx.save();
980
+ ctx.translate(x, y);
981
+ if (text.is_mirrored) ctx.scale(-1, 1);
982
+ if (rotation !== 0) ctx.rotate(-rotation * (Math.PI / 180));
983
+ ctx.lineWidth = layout.strokeWidth;
984
+ ctx.lineCap = "round";
985
+ ctx.lineJoin = "round";
986
+ if (text.is_knockout) {
987
+ const paddingLeft = padding.left * scale2;
988
+ const paddingRight = padding.right * scale2;
989
+ const paddingTop = padding.top * scale2;
990
+ const paddingBottom = padding.bottom * scale2;
991
+ const xOffset = startX - paddingLeft;
992
+ const yOffset = -(layout.height / 2) - layout.strokeWidth / 2 - paddingTop;
993
+ const knockoutWidth = totalWidth + paddingLeft + paddingRight;
994
+ const knockoutHeight = totalHeight + paddingTop + paddingBottom;
995
+ ctx.fillStyle = textColor;
996
+ ctx.fillRect(xOffset, yOffset, knockoutWidth, knockoutHeight);
997
+ const previousCompositeOperation = ctx.globalCompositeOperation;
998
+ ctx.globalCompositeOperation = "destination-out";
999
+ ctx.fillStyle = "rgba(0,0,0,1)";
1000
+ ctx.strokeStyle = "rgba(0,0,0,1)";
1001
+ strokeAlphabetText(ctx, content, layout, startX, startY);
1002
+ if (previousCompositeOperation) {
1003
+ ctx.globalCompositeOperation = previousCompositeOperation;
1004
+ } else {
1005
+ ctx.globalCompositeOperation = "source-over";
1006
+ }
1007
+ } else {
1008
+ ctx.strokeStyle = textColor;
1009
+ strokeAlphabetText(ctx, content, layout, startX, startY);
1010
+ }
1011
+ ctx.restore();
1012
+ }
1013
+
1014
+ // lib/drawer/elements/pcb-fabrication-note-text.ts
1015
+ var DEFAULT_FABRICATION_NOTE_COLOR = "rgba(255,255,255,0.5)";
1016
+ function layerToColor4(layer, colorMap) {
1017
+ return DEFAULT_FABRICATION_NOTE_COLOR;
1018
+ }
1019
+ function drawPcbFabricationNoteText(params) {
1020
+ const { ctx, text, transform, colorMap } = params;
1021
+ const defaultColor = layerToColor4(text.layer, colorMap);
1022
+ const color = text.color ?? defaultColor;
1023
+ const fontSize = text.font_size;
1024
+ drawText({
1025
+ ctx,
1026
+ text: text.text,
1027
+ x: text.anchor_position.x,
1028
+ y: text.anchor_position.y,
1029
+ fontSize,
1030
+ color,
1031
+ transform,
1032
+ anchorAlignment: text.anchor_alignment
1033
+ });
1034
+ }
1035
+
1036
+ // lib/drawer/elements/pcb-fabrication-note-rect.ts
1037
+ function drawPcbFabricationNoteRect(params) {
1038
+ const { ctx, rect, transform, colorMap } = params;
1039
+ const defaultColor = "rgba(255,255,255,0.5)";
1040
+ const color = rect.color ?? defaultColor;
1041
+ const isFilled = rect.is_filled ?? false;
1042
+ const hasStroke = rect.has_stroke ?? true;
1043
+ const isStrokeDashed = rect.is_stroke_dashed ?? false;
1044
+ drawRect({
1045
+ ctx,
1046
+ center: rect.center,
1047
+ width: rect.width,
1048
+ height: rect.height,
1049
+ fill: isFilled ? color : void 0,
1050
+ stroke: hasStroke ? color : void 0,
1051
+ strokeWidth: hasStroke ? rect.stroke_width : void 0,
1052
+ borderRadius: rect.corner_radius,
1053
+ transform,
1054
+ isStrokeDashed
1055
+ });
1056
+ }
1057
+
1058
+ // lib/drawer/elements/pcb-fabrication-note-path.ts
1059
+ function drawPcbFabricationNotePath(params) {
1060
+ const { ctx, path, transform, colorMap } = params;
1061
+ const defaultColor = "rgba(255,255,255,0.5)";
1062
+ const color = path.color ?? defaultColor;
1063
+ if (!path.route || path.route.length < 2) return;
1064
+ for (let i = 0; i < path.route.length - 1; i++) {
1065
+ const start = path.route[i];
1066
+ const end = path.route[i + 1];
1067
+ if (!start || !end) continue;
1068
+ drawLine({
1069
+ ctx,
1070
+ start: { x: start.x, y: start.y },
1071
+ end: { x: end.x, y: end.y },
1072
+ strokeWidth: path.stroke_width ?? 0.1,
1073
+ stroke: color,
1074
+ transform
1075
+ });
1076
+ }
1077
+ }
1078
+
797
1079
  // lib/drawer/CircuitToCanvasDrawer.ts
798
1080
  var CircuitToCanvasDrawer = class {
799
1081
  ctx;
@@ -967,6 +1249,38 @@ var CircuitToCanvasDrawer = class {
967
1249
  colorMap: this.colorMap
968
1250
  });
969
1251
  }
1252
+ if (element.type === "pcb_copper_text") {
1253
+ drawPcbCopperText({
1254
+ ctx: this.ctx,
1255
+ text: element,
1256
+ transform: this.realToCanvasMat,
1257
+ colorMap: this.colorMap
1258
+ });
1259
+ }
1260
+ if (element.type === "pcb_fabrication_note_text") {
1261
+ drawPcbFabricationNoteText({
1262
+ ctx: this.ctx,
1263
+ text: element,
1264
+ transform: this.realToCanvasMat,
1265
+ colorMap: this.colorMap
1266
+ });
1267
+ }
1268
+ if (element.type === "pcb_fabrication_note_rect") {
1269
+ drawPcbFabricationNoteRect({
1270
+ ctx: this.ctx,
1271
+ rect: element,
1272
+ transform: this.realToCanvasMat,
1273
+ colorMap: this.colorMap
1274
+ });
1275
+ }
1276
+ if (element.type === "pcb_fabrication_note_path") {
1277
+ drawPcbFabricationNotePath({
1278
+ ctx: this.ctx,
1279
+ path: element,
1280
+ transform: this.realToCanvasMat,
1281
+ colorMap: this.colorMap
1282
+ });
1283
+ }
970
1284
  }
971
1285
  };
972
1286
  export {
@@ -978,7 +1292,11 @@ export {
978
1292
  drawPath,
979
1293
  drawPcbBoard,
980
1294
  drawPcbCopperPour,
1295
+ drawPcbCopperText,
981
1296
  drawPcbCutout,
1297
+ drawPcbFabricationNotePath,
1298
+ drawPcbFabricationNoteRect,
1299
+ drawPcbFabricationNoteText,
982
1300
  drawPcbHole,
983
1301
  drawPcbPlatedHole,
984
1302
  drawPcbSilkscreenCircle,
@@ -991,5 +1309,9 @@ export {
991
1309
  drawPcbVia,
992
1310
  drawPill,
993
1311
  drawPolygon,
994
- drawRect
1312
+ drawRect,
1313
+ drawText,
1314
+ getAlphabetLayout,
1315
+ getTextStartPosition,
1316
+ strokeAlphabetText
995
1317
  };
@@ -13,6 +13,10 @@ import type {
13
13
  PcbSilkscreenPath,
14
14
  PcbCutout,
15
15
  PcbCopperPour,
16
+ PcbCopperText,
17
+ PcbFabricationNoteText,
18
+ PcbFabricationNoteRect,
19
+ PcbFabricationNotePath,
16
20
  } from "circuit-json"
17
21
  import { identity, compose, translate, scale } from "transformation-matrix"
18
22
  import type { Matrix } from "transformation-matrix"
@@ -38,6 +42,10 @@ import {
38
42
  } from "./elements/pcb-silkscreen"
39
43
  import { drawPcbCutout } from "./elements/pcb-cutout"
40
44
  import { drawPcbCopperPour } from "./elements/pcb-copper-pour"
45
+ import { drawPcbCopperText } from "./elements/pcb-copper-text"
46
+ import { drawPcbFabricationNoteText } from "./elements/pcb-fabrication-note-text"
47
+ import { drawPcbFabricationNoteRect } from "./elements/pcb-fabrication-note-rect"
48
+ import { drawPcbFabricationNotePath } from "./elements/pcb-fabrication-note-path"
41
49
 
42
50
  export interface DrawElementsOptions {
43
51
  layers?: string[]
@@ -252,5 +260,41 @@ export class CircuitToCanvasDrawer {
252
260
  colorMap: this.colorMap,
253
261
  })
254
262
  }
263
+
264
+ if (element.type === "pcb_copper_text") {
265
+ drawPcbCopperText({
266
+ ctx: this.ctx,
267
+ text: element as PcbCopperText,
268
+ transform: this.realToCanvasMat,
269
+ colorMap: this.colorMap,
270
+ })
271
+ }
272
+
273
+ if (element.type === "pcb_fabrication_note_text") {
274
+ drawPcbFabricationNoteText({
275
+ ctx: this.ctx,
276
+ text: element as PcbFabricationNoteText,
277
+ transform: this.realToCanvasMat,
278
+ colorMap: this.colorMap,
279
+ })
280
+ }
281
+
282
+ if (element.type === "pcb_fabrication_note_rect") {
283
+ drawPcbFabricationNoteRect({
284
+ ctx: this.ctx,
285
+ rect: element as PcbFabricationNoteRect,
286
+ transform: this.realToCanvasMat,
287
+ colorMap: this.colorMap,
288
+ })
289
+ }
290
+
291
+ if (element.type === "pcb_fabrication_note_path") {
292
+ drawPcbFabricationNotePath({
293
+ ctx: this.ctx,
294
+ path: element as PcbFabricationNotePath,
295
+ transform: this.realToCanvasMat,
296
+ colorMap: this.colorMap,
297
+ })
298
+ }
255
299
  }
256
300
  }
@@ -32,3 +32,23 @@ export {
32
32
  drawPcbCopperPour,
33
33
  type DrawPcbCopperPourParams,
34
34
  } from "./pcb-copper-pour"
35
+
36
+ export {
37
+ drawPcbCopperText,
38
+ type DrawPcbCopperTextParams,
39
+ } from "./pcb-copper-text"
40
+
41
+ export {
42
+ drawPcbFabricationNoteText,
43
+ type DrawPcbFabricationNoteTextParams,
44
+ } from "./pcb-fabrication-note-text"
45
+
46
+ export {
47
+ drawPcbFabricationNoteRect,
48
+ type DrawPcbFabricationNoteRectParams,
49
+ } from "./pcb-fabrication-note-rect"
50
+
51
+ export {
52
+ drawPcbFabricationNotePath,
53
+ type DrawPcbFabricationNotePathParams,
54
+ } from "./pcb-fabrication-note-path"