circuit-to-canvas 0.0.49 → 0.0.51

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 (52) hide show
  1. package/dist/index.d.ts +13 -5
  2. package/dist/index.js +1450 -1226
  3. package/lib/drawer/CircuitToCanvasDrawer.ts +262 -312
  4. package/lib/drawer/elements/helper-functions/draw-pill.ts +39 -0
  5. package/lib/drawer/elements/helper-functions/draw-polygon.ts +25 -0
  6. package/lib/drawer/elements/helper-functions/draw-rounded-rect.ts +34 -0
  7. package/lib/drawer/elements/helper-functions/index.ts +3 -0
  8. package/lib/drawer/elements/pcb-board.ts +13 -3
  9. package/lib/drawer/elements/pcb-hole.ts +56 -338
  10. package/lib/drawer/elements/pcb-plated-hole.ts +154 -442
  11. package/lib/drawer/elements/pcb-smtpad.ts +5 -271
  12. package/lib/drawer/elements/pcb-soldermask/board.ts +44 -0
  13. package/lib/drawer/elements/pcb-soldermask/cutout.ts +74 -0
  14. package/lib/drawer/elements/pcb-soldermask/hole.ts +288 -0
  15. package/lib/drawer/elements/pcb-soldermask/index.ts +140 -0
  16. package/lib/drawer/elements/pcb-soldermask/plated-hole.ts +365 -0
  17. package/lib/drawer/elements/pcb-soldermask/smt-pad.ts +354 -0
  18. package/lib/drawer/elements/pcb-soldermask/via.ts +27 -0
  19. package/lib/drawer/elements/soldermask-margin.ts +39 -8
  20. package/package.json +2 -2
  21. package/tests/board-snapshot/__snapshots__/usb-c-flashlight-board.snap.png +0 -0
  22. package/tests/board-snapshot/usb-c-flashlight-board.test.ts +1 -0
  23. package/tests/elements/__snapshots__/board-with-elements.snap.png +0 -0
  24. package/tests/elements/__snapshots__/brep-copper-pours.snap.png +0 -0
  25. package/tests/elements/__snapshots__/custom-outline-board.snap.png +0 -0
  26. package/tests/elements/__snapshots__/oval-plated-hole.snap.png +0 -0
  27. package/tests/elements/__snapshots__/pcb-board.snap.png +0 -0
  28. package/tests/elements/__snapshots__/pcb-comprehensive-soldermask-margin.snap.png +0 -0
  29. package/tests/elements/__snapshots__/pcb-fabrication-note-dimension.snap.png +0 -0
  30. package/tests/elements/__snapshots__/pcb-hole-soldermask-margin.snap.png +0 -0
  31. package/tests/elements/__snapshots__/pcb-keepout-layer-filter.snap.png +0 -0
  32. package/tests/elements/__snapshots__/pcb-keepout-multiple-layers.snap.png +0 -0
  33. package/tests/elements/__snapshots__/pcb-keepout-rect-and-circle.snap.png +0 -0
  34. package/tests/elements/__snapshots__/pcb-keepout-with-group-id.snap.png +0 -0
  35. package/tests/elements/__snapshots__/pcb-no-soldermask.snap.png +0 -0
  36. package/tests/elements/__snapshots__/pcb-plated-hole-soldermask-margin.snap.png +0 -0
  37. package/tests/elements/__snapshots__/pcb-plated-hole.snap.png +0 -0
  38. package/tests/elements/__snapshots__/pcb-silkscreen-on-component.snap.png +0 -0
  39. package/tests/elements/__snapshots__/pcb-silkscreen-oval.snap.png +0 -0
  40. package/tests/elements/__snapshots__/pcb-smtpad-asymmetric-soldermask-margin.snap.png +0 -0
  41. package/tests/elements/__snapshots__/pcb-smtpad-soldermask-coverage.snap.png +0 -0
  42. package/tests/elements/__snapshots__/pcb-smtpad-soldermask-margin.snap.png +0 -0
  43. package/tests/elements/__snapshots__/pill-plated-hole.snap.png +0 -0
  44. package/tests/elements/pcb-comprehensive-soldermask-margin.test.ts +2 -2
  45. package/tests/elements/pcb-hole-soldermask-margin.test.ts +155 -2
  46. package/tests/elements/pcb-no-soldermask.test.ts +1281 -0
  47. package/tests/elements/pcb-plated-hole-soldermask-margin.test.ts +1 -1
  48. package/tests/elements/pcb-plated-hole.test.ts +40 -4
  49. package/tests/elements/pcb-smtpad-asymmetric-soldermask-margin.test.ts +140 -0
  50. package/tests/elements/pcb-smtpad-soldermask-coverage.test.ts +1 -1
  51. package/tests/elements/pcb-smtpad-soldermask-margin.test.ts +18 -2
  52. package/tests/fixtures/getStackedPngSvgComparison.ts +8 -2
@@ -217,7 +217,7 @@ test("draw plated holes with positive and negative soldermask margins", async ()
217
217
  ]
218
218
 
219
219
  drawer.setCameraBounds({ minX: -8, maxX: 8, minY: -6.5, maxY: 6.5 })
220
- drawer.drawElements(circuit)
220
+ drawer.drawElements(circuit, { showSoldermask: true })
221
221
 
222
222
  await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
223
223
  import.meta.path,
@@ -1,6 +1,6 @@
1
1
  import { expect, test } from "bun:test"
2
2
  import { createCanvas } from "@napi-rs/canvas"
3
- import type { PcbPlatedHole } from "circuit-json"
3
+ import type { PcbBoard, PcbPlatedHole } from "circuit-json"
4
4
  import { CircuitToCanvasDrawer } from "../../lib/drawer"
5
5
 
6
6
  test("draw circular plated hole", async () => {
@@ -22,7 +22,19 @@ test("draw circular plated hole", async () => {
22
22
  layers: ["top", "bottom"],
23
23
  }
24
24
 
25
- drawer.drawElements([hole])
25
+ const board: PcbBoard = {
26
+ type: "pcb_board",
27
+ pcb_board_id: "board1",
28
+ center: { x: 50, y: 50 },
29
+ width: 100,
30
+ height: 100,
31
+ thickness: 1.6,
32
+ num_layers: 2,
33
+ material: "fr4",
34
+ }
35
+
36
+ drawer.setCameraBounds({ minX: 0, maxX: 100, minY: 0, maxY: 100 })
37
+ drawer.drawElements([board, hole])
26
38
 
27
39
  await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
28
40
  import.meta.path,
@@ -51,7 +63,19 @@ test("draw oval plated hole", async () => {
51
63
  ccw_rotation: 0,
52
64
  }
53
65
 
54
- drawer.drawElements([hole])
66
+ const board: PcbBoard = {
67
+ type: "pcb_board",
68
+ pcb_board_id: "board1",
69
+ center: { x: 50, y: 50 },
70
+ width: 100,
71
+ height: 100,
72
+ thickness: 1.6,
73
+ num_layers: 2,
74
+ material: "fr4",
75
+ }
76
+
77
+ drawer.setCameraBounds({ minX: 0, maxX: 100, minY: 0, maxY: 100 })
78
+ drawer.drawElements([board, hole])
55
79
 
56
80
  await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
57
81
  import.meta.path,
@@ -81,7 +105,19 @@ test("draw pill plated hole", async () => {
81
105
  ccw_rotation: 0,
82
106
  }
83
107
 
84
- drawer.drawElements([hole])
108
+ const board: PcbBoard = {
109
+ type: "pcb_board",
110
+ pcb_board_id: "board1",
111
+ center: { x: 50, y: 50 },
112
+ width: 100,
113
+ height: 100,
114
+ thickness: 1.6,
115
+ num_layers: 2,
116
+ material: "fr4",
117
+ }
118
+
119
+ drawer.setCameraBounds({ minX: 0, maxX: 100, minY: 0, maxY: 100 })
120
+ drawer.drawElements([board, hole])
85
121
 
86
122
  await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
87
123
  import.meta.path,
@@ -0,0 +1,140 @@
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("draw smt pads with asymmetric soldermask margins", async () => {
7
+ const canvas = createCanvas(800, 600)
8
+ const ctx = canvas.getContext("2d")
9
+ const drawer = new CircuitToCanvasDrawer(ctx)
10
+
11
+ ctx.fillStyle = "#1a1a1a"
12
+ ctx.fillRect(0, 0, 800, 600)
13
+
14
+ const circuit: AnyCircuitElement[] = [
15
+ {
16
+ type: "pcb_board",
17
+ pcb_board_id: "board0",
18
+ center: { x: 0, y: 0 },
19
+ width: 14,
20
+ height: 10,
21
+ thickness: 1.6,
22
+ num_layers: 2,
23
+ material: "fr4",
24
+ },
25
+ // Rectangle with asymmetric positive margins
26
+ {
27
+ type: "pcb_smtpad",
28
+ pcb_smtpad_id: "pad_rect_pos_asym",
29
+ shape: "rect",
30
+ layer: "top",
31
+ x: -4,
32
+ y: 2,
33
+ width: 2,
34
+ height: 1,
35
+ soldermask_margin_left: 0.5,
36
+ soldermask_margin_right: 0.1,
37
+ soldermask_margin_top: 0.2,
38
+ soldermask_margin_bottom: 0.8,
39
+ },
40
+ // Rectangle with asymmetric negative margins
41
+ {
42
+ type: "pcb_smtpad",
43
+ pcb_smtpad_id: "pad_rect_neg_asym",
44
+ shape: "rect",
45
+ layer: "top",
46
+ x: -4,
47
+ y: -2,
48
+ width: 2,
49
+ height: 1.5,
50
+ soldermask_margin_left: -0.2,
51
+ soldermask_margin_right: -0.6,
52
+ soldermask_margin_top: -0.1,
53
+ soldermask_margin_bottom: -0.4,
54
+ },
55
+ // Rotated rectangle with asymmetric positive margins
56
+ {
57
+ type: "pcb_smtpad",
58
+ pcb_smtpad_id: "pad_rot_rect_pos_asym",
59
+ shape: "rotated_rect",
60
+ layer: "top",
61
+ x: 4,
62
+ y: 2,
63
+ width: 2,
64
+ height: 1,
65
+ ccw_rotation: 45,
66
+ soldermask_margin_left: 0.8,
67
+ soldermask_margin_right: 0.2,
68
+ soldermask_margin_top: 0.1,
69
+ soldermask_margin_bottom: 0.5,
70
+ },
71
+ // Rotated rectangle with asymmetric negative margins
72
+ {
73
+ type: "pcb_smtpad",
74
+ pcb_smtpad_id: "pad_rot_rect_neg_asym",
75
+ shape: "rotated_rect",
76
+ layer: "top",
77
+ x: 4,
78
+ y: -2,
79
+ width: 2,
80
+ height: 1.5,
81
+ ccw_rotation: 30,
82
+ soldermask_margin_left: -0.5,
83
+ soldermask_margin_right: -0.1,
84
+ soldermask_margin_top: -0.4,
85
+ soldermask_margin_bottom: -0.2,
86
+ },
87
+ // Labels
88
+ {
89
+ type: "pcb_silkscreen_text",
90
+ pcb_silkscreen_text_id: "text1",
91
+ pcb_component_id: "comp1",
92
+ layer: "top",
93
+ anchor_position: { x: -4, y: 3.5 },
94
+ anchor_alignment: "center",
95
+ text: "Rect Pos Asym",
96
+ font_size: 0.4,
97
+ font: "tscircuit2024",
98
+ },
99
+ {
100
+ type: "pcb_silkscreen_text",
101
+ pcb_silkscreen_text_id: "text2",
102
+ pcb_component_id: "comp1",
103
+ layer: "top",
104
+ anchor_position: { x: -4, y: -3.8 },
105
+ anchor_alignment: "center",
106
+ text: "Rect Neg Asym",
107
+ font_size: 0.4,
108
+ font: "tscircuit2024",
109
+ },
110
+ {
111
+ type: "pcb_silkscreen_text",
112
+ pcb_silkscreen_text_id: "text3",
113
+ pcb_component_id: "comp1",
114
+ layer: "top",
115
+ anchor_position: { x: 4, y: 3.8 },
116
+ anchor_alignment: "center",
117
+ text: "Rot Rect Pos Asym",
118
+ font_size: 0.4,
119
+ font: "tscircuit2024",
120
+ },
121
+ {
122
+ type: "pcb_silkscreen_text",
123
+ pcb_silkscreen_text_id: "text4",
124
+ pcb_component_id: "comp1",
125
+ layer: "top",
126
+ anchor_position: { x: 4, y: -3.8 },
127
+ anchor_alignment: "center",
128
+ text: "Rot Rect Neg Asym",
129
+ font_size: 0.4,
130
+ font: "tscircuit2024",
131
+ },
132
+ ]
133
+
134
+ drawer.setCameraBounds({ minX: -7, maxX: 7, minY: -5, maxY: 5 })
135
+ drawer.drawElements(circuit, { showSoldermask: true })
136
+
137
+ await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
138
+ import.meta.path,
139
+ )
140
+ })
@@ -154,7 +154,7 @@ test("draw smt pads fully covered with soldermask and board with soldermask", as
154
154
  ]
155
155
 
156
156
  drawer.setCameraBounds({ minX: -7, maxX: 7, minY: -5, maxY: 5 })
157
- drawer.drawElements(circuit)
157
+ drawer.drawElements(circuit, { showSoldermask: true })
158
158
 
159
159
  await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
160
160
  import.meta.path,
@@ -1,5 +1,6 @@
1
1
  import { expect, test } from "bun:test"
2
2
  import { createCanvas } from "@napi-rs/canvas"
3
+ import type { AnyCircuitElement } from "circuit-json"
3
4
  import { CircuitToCanvasDrawer } from "../../lib/drawer"
4
5
 
5
6
  test("draw smt pads with positive and negative soldermask margins", async () => {
@@ -10,13 +11,16 @@ test("draw smt pads with positive and negative soldermask margins", async () =>
10
11
  ctx.fillStyle = "#1a1a1a"
11
12
  ctx.fillRect(0, 0, 800, 600)
12
13
 
13
- const circuit: any = [
14
+ const circuit: AnyCircuitElement[] = [
14
15
  {
15
16
  type: "pcb_board",
16
17
  pcb_board_id: "board0",
17
18
  center: { x: 0, y: 0 },
18
19
  width: 14,
19
20
  height: 10,
21
+ thickness: 1.6,
22
+ num_layers: 2,
23
+ material: "fr4",
20
24
  },
21
25
  // Rectangle with positive margin (mask extends beyond pad)
22
26
  {
@@ -94,62 +98,74 @@ test("draw smt pads with positive and negative soldermask margins", async () =>
94
98
  {
95
99
  type: "pcb_silkscreen_text",
96
100
  pcb_silkscreen_text_id: "text_rect_pos",
101
+ pcb_component_id: "comp1",
97
102
  layer: "top",
98
103
  anchor_position: { x: -4, y: 3.2 },
99
104
  anchor_alignment: "center",
100
105
  text: "+0.2mm",
101
106
  font_size: 0.4,
107
+ font: "tscircuit2024",
102
108
  },
103
109
  {
104
110
  type: "pcb_silkscreen_text",
105
111
  pcb_silkscreen_text_id: "text_circle_pos",
112
+ pcb_component_id: "comp1",
106
113
  layer: "top",
107
114
  anchor_position: { x: 0, y: 3.2 },
108
115
  anchor_alignment: "center",
109
116
  text: "+0.15mm",
110
117
  font_size: 0.4,
118
+ font: "tscircuit2024",
111
119
  },
112
120
  {
113
121
  type: "pcb_silkscreen_text",
114
122
  pcb_silkscreen_text_id: "text_pill_pos",
123
+ pcb_component_id: "comp1",
115
124
  layer: "top",
116
125
  anchor_position: { x: 4, y: 3.2 },
117
126
  anchor_alignment: "center",
118
127
  text: "+0.1mm",
119
128
  font_size: 0.4,
129
+ font: "tscircuit2024",
120
130
  },
121
131
  // Silkscreen labels for negative margin pads (bottom row)
122
132
  {
123
133
  type: "pcb_silkscreen_text",
124
134
  pcb_silkscreen_text_id: "text_rect_neg",
135
+ pcb_component_id: "comp1",
125
136
  layer: "top",
126
137
  anchor_position: { x: -4, y: -3.2 },
127
138
  anchor_alignment: "center",
128
139
  text: "-0.15mm",
129
140
  font_size: 0.4,
141
+ font: "tscircuit2024",
130
142
  },
131
143
  {
132
144
  type: "pcb_silkscreen_text",
133
145
  pcb_silkscreen_text_id: "text_circle_neg",
146
+ pcb_component_id: "comp1",
134
147
  layer: "top",
135
148
  anchor_position: { x: 0, y: -3.2 },
136
149
  anchor_alignment: "center",
137
150
  text: "-0.2mm",
138
151
  font_size: 0.4,
152
+ font: "tscircuit2024",
139
153
  },
140
154
  {
141
155
  type: "pcb_silkscreen_text",
142
156
  pcb_silkscreen_text_id: "text_pill_neg",
157
+ pcb_component_id: "comp1",
143
158
  layer: "top",
144
159
  anchor_position: { x: 4, y: -3.2 },
145
160
  anchor_alignment: "center",
146
161
  text: "-0.12mm",
147
162
  font_size: 0.4,
163
+ font: "tscircuit2024",
148
164
  },
149
165
  ]
150
166
 
151
167
  drawer.setCameraBounds({ minX: -7, maxX: 7, minY: -5, maxY: 5 })
152
- drawer.drawElements(circuit)
168
+ drawer.drawElements(circuit, { showSoldermask: true })
153
169
 
154
170
  await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
155
171
  import.meta.path,
@@ -10,6 +10,7 @@ export interface StackedPngSvgComparisonOptions {
10
10
  width?: number
11
11
  height?: number
12
12
  padding?: number
13
+ showSoldermask?: boolean
13
14
  }
14
15
 
15
16
  /**
@@ -23,7 +24,12 @@ export async function getStackedPngSvgComparison(
23
24
  circuitJson: AnyCircuitElement[],
24
25
  options: StackedPngSvgComparisonOptions = {},
25
26
  ): Promise<Buffer> {
26
- const { width = 400, height = 800, padding = 4 } = options
27
+ const {
28
+ width = 400,
29
+ height = 800,
30
+ padding = 4,
31
+ showSoldermask = false,
32
+ } = options
27
33
 
28
34
  const bounds = getBoundsOfPcbElements(circuitJson)
29
35
 
@@ -41,7 +47,7 @@ export async function getStackedPngSvgComparison(
41
47
  minY: bounds.minY,
42
48
  maxY: bounds.maxY,
43
49
  })
44
- drawer.drawElements(circuitJson)
50
+ drawer.drawElements(circuitJson, { showSoldermask })
45
51
 
46
52
  const canvasPng = canvas.toBuffer("image/png")
47
53