circuit-to-canvas 0.0.28 → 0.0.29

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.js CHANGED
@@ -196,6 +196,30 @@ function drawPill(params) {
196
196
  ctx.restore();
197
197
  }
198
198
 
199
+ // lib/drawer/shapes/polygon.ts
200
+ import { applyToPoint as applyToPoint5 } from "transformation-matrix";
201
+ function drawPolygon(params) {
202
+ const { ctx, points, fill, realToCanvasMat } = params;
203
+ if (points.length < 3) return;
204
+ ctx.beginPath();
205
+ const canvasPoints = points.map(
206
+ (p) => applyToPoint5(realToCanvasMat, [p.x, p.y])
207
+ );
208
+ const firstPoint = canvasPoints[0];
209
+ if (!firstPoint) return;
210
+ const [firstX, firstY] = firstPoint;
211
+ ctx.moveTo(firstX, firstY);
212
+ for (let i = 1; i < canvasPoints.length; i++) {
213
+ const point = canvasPoints[i];
214
+ if (!point) continue;
215
+ const [x, y] = point;
216
+ ctx.lineTo(x, y);
217
+ }
218
+ ctx.closePath();
219
+ ctx.fillStyle = fill;
220
+ ctx.fill();
221
+ }
222
+
199
223
  // lib/drawer/elements/pcb-plated-hole.ts
200
224
  function drawPcbPlatedHole(params) {
201
225
  const { ctx, hole, realToCanvasMat, colorMap } = params;
@@ -325,6 +349,61 @@ function drawPcbPlatedHole(params) {
325
349
  });
326
350
  return;
327
351
  }
352
+ if (hole.shape === "hole_with_polygon_pad") {
353
+ const padOutline = hole.pad_outline;
354
+ if (padOutline && padOutline.length >= 3) {
355
+ const padPoints = padOutline.map((point) => ({
356
+ x: hole.x + point.x,
357
+ y: hole.y + point.y
358
+ }));
359
+ drawPolygon({
360
+ ctx,
361
+ points: padPoints,
362
+ fill: colorMap.copper.top,
363
+ realToCanvasMat
364
+ });
365
+ }
366
+ const holeX = hole.x + (hole.hole_offset_x ?? 0);
367
+ const holeY = hole.y + (hole.hole_offset_y ?? 0);
368
+ const holeShape = hole.hole_shape;
369
+ if (holeShape === "circle") {
370
+ drawCircle({
371
+ ctx,
372
+ center: { x: holeX, y: holeY },
373
+ radius: (hole.hole_diameter ?? 0) / 2,
374
+ fill: colorMap.drill,
375
+ realToCanvasMat
376
+ });
377
+ } else if (holeShape === "oval") {
378
+ drawOval({
379
+ ctx,
380
+ center: { x: holeX, y: holeY },
381
+ width: hole.hole_width ?? 0,
382
+ height: hole.hole_height ?? 0,
383
+ fill: colorMap.drill,
384
+ realToCanvasMat
385
+ });
386
+ } else if (holeShape === "pill") {
387
+ drawPill({
388
+ ctx,
389
+ center: { x: holeX, y: holeY },
390
+ width: hole.hole_width ?? 0,
391
+ height: hole.hole_height ?? 0,
392
+ fill: colorMap.drill,
393
+ realToCanvasMat
394
+ });
395
+ } else if (holeShape === "rotated_pill") {
396
+ drawPill({
397
+ ctx,
398
+ center: { x: holeX, y: holeY },
399
+ width: hole.hole_width ?? 0,
400
+ height: hole.hole_height ?? 0,
401
+ fill: colorMap.drill,
402
+ realToCanvasMat
403
+ });
404
+ }
405
+ return;
406
+ }
328
407
  }
329
408
 
330
409
  // lib/drawer/elements/pcb-via.ts
@@ -417,30 +496,6 @@ function drawPcbHole(params) {
417
496
  }
418
497
  }
419
498
 
420
- // lib/drawer/shapes/polygon.ts
421
- import { applyToPoint as applyToPoint5 } from "transformation-matrix";
422
- function drawPolygon(params) {
423
- const { ctx, points, fill, realToCanvasMat } = params;
424
- if (points.length < 3) return;
425
- ctx.beginPath();
426
- const canvasPoints = points.map(
427
- (p) => applyToPoint5(realToCanvasMat, [p.x, p.y])
428
- );
429
- const firstPoint = canvasPoints[0];
430
- if (!firstPoint) return;
431
- const [firstX, firstY] = firstPoint;
432
- ctx.moveTo(firstX, firstY);
433
- for (let i = 1; i < canvasPoints.length; i++) {
434
- const point = canvasPoints[i];
435
- if (!point) continue;
436
- const [x, y] = point;
437
- ctx.lineTo(x, y);
438
- }
439
- ctx.closePath();
440
- ctx.fillStyle = fill;
441
- ctx.fill();
442
- }
443
-
444
499
  // lib/drawer/elements/pcb-smtpad.ts
445
500
  function layerToColor(layer, colorMap) {
446
501
  return colorMap.copper[layer] ?? colorMap.copper.top;
@@ -5,6 +5,7 @@ import { drawCircle } from "../shapes/circle"
5
5
  import { drawRect } from "../shapes/rect"
6
6
  import { drawOval } from "../shapes/oval"
7
7
  import { drawPill } from "../shapes/pill"
8
+ import { drawPolygon } from "../shapes/polygon"
8
9
 
9
10
  export interface DrawPcbPlatedHoleParams {
10
11
  ctx: CanvasContext
@@ -96,12 +97,12 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
96
97
  height: hole.rect_pad_height,
97
98
  fill: colorMap.copper.top,
98
99
  realToCanvasMat,
99
- borderRadius: (hole as any).rect_border_radius ?? 0,
100
+ borderRadius: hole.rect_border_radius ?? 0,
100
101
  })
101
102
 
102
103
  // Draw circular drill hole (with offset)
103
- const holeX = hole.x + ((hole as any).hole_offset_x ?? 0)
104
- const holeY = hole.y + ((hole as any).hole_offset_y ?? 0)
104
+ const holeX = hole.x + (hole.hole_offset_x ?? 0)
105
+ const holeY = hole.y + (hole.hole_offset_y ?? 0)
105
106
  drawCircle({
106
107
  ctx,
107
108
  center: { x: holeX, y: holeY },
@@ -121,12 +122,12 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
121
122
  height: hole.rect_pad_height,
122
123
  fill: colorMap.copper.top,
123
124
  realToCanvasMat,
124
- borderRadius: (hole as any).rect_border_radius ?? 0,
125
+ borderRadius: hole.rect_border_radius ?? 0,
125
126
  })
126
127
 
127
128
  // Draw pill drill hole (with offset)
128
- const holeX = hole.x + ((hole as any).hole_offset_x ?? 0)
129
- const holeY = hole.y + ((hole as any).hole_offset_y ?? 0)
129
+ const holeX = hole.x + (hole.hole_offset_x ?? 0)
130
+ const holeY = hole.y + (hole.hole_offset_y ?? 0)
130
131
  drawPill({
131
132
  ctx,
132
133
  center: { x: holeX, y: holeY },
@@ -147,13 +148,13 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
147
148
  height: hole.rect_pad_height,
148
149
  fill: colorMap.copper.top,
149
150
  realToCanvasMat,
150
- borderRadius: (hole as any).rect_border_radius ?? 0,
151
+ borderRadius: hole.rect_border_radius ?? 0,
151
152
  rotation: hole.rect_ccw_rotation,
152
153
  })
153
154
 
154
155
  // Draw rotated pill drill hole (with offset)
155
- const holeX = hole.x + ((hole as any).hole_offset_x ?? 0)
156
- const holeY = hole.y + ((hole as any).hole_offset_y ?? 0)
156
+ const holeX = hole.x + (hole.hole_offset_x ?? 0)
157
+ const holeY = hole.y + (hole.hole_offset_y ?? 0)
157
158
  drawPill({
158
159
  ctx,
159
160
  center: { x: holeX, y: holeY },
@@ -165,4 +166,65 @@ export function drawPcbPlatedHole(params: DrawPcbPlatedHoleParams): void {
165
166
  })
166
167
  return
167
168
  }
169
+
170
+ if (hole.shape === "hole_with_polygon_pad") {
171
+ // Draw polygon pad
172
+ const padOutline = hole.pad_outline
173
+ if (padOutline && padOutline.length >= 3) {
174
+ // Transform pad_outline points to be relative to hole.x, hole.y
175
+ const padPoints = padOutline.map((point: { x: number; y: number }) => ({
176
+ x: hole.x + point.x,
177
+ y: hole.y + point.y,
178
+ }))
179
+ drawPolygon({
180
+ ctx,
181
+ points: padPoints,
182
+ fill: colorMap.copper.top,
183
+ realToCanvasMat,
184
+ })
185
+ }
186
+
187
+ // Draw drill hole (with offset)
188
+ const holeX = hole.x + (hole.hole_offset_x ?? 0)
189
+ const holeY = hole.y + (hole.hole_offset_y ?? 0)
190
+ const holeShape = hole.hole_shape
191
+
192
+ if (holeShape === "circle") {
193
+ drawCircle({
194
+ ctx,
195
+ center: { x: holeX, y: holeY },
196
+ radius: (hole.hole_diameter ?? 0) / 2,
197
+ fill: colorMap.drill,
198
+ realToCanvasMat,
199
+ })
200
+ } else if (holeShape === "oval") {
201
+ drawOval({
202
+ ctx,
203
+ center: { x: holeX, y: holeY },
204
+ width: hole.hole_width ?? 0,
205
+ height: hole.hole_height ?? 0,
206
+ fill: colorMap.drill,
207
+ realToCanvasMat,
208
+ })
209
+ } else if (holeShape === "pill") {
210
+ drawPill({
211
+ ctx,
212
+ center: { x: holeX, y: holeY },
213
+ width: hole.hole_width ?? 0,
214
+ height: hole.hole_height ?? 0,
215
+ fill: colorMap.drill,
216
+ realToCanvasMat,
217
+ })
218
+ } else if (holeShape === "rotated_pill") {
219
+ drawPill({
220
+ ctx,
221
+ center: { x: holeX, y: holeY },
222
+ width: hole.hole_width ?? 0,
223
+ height: hole.hole_height ?? 0,
224
+ fill: colorMap.drill,
225
+ realToCanvasMat,
226
+ })
227
+ }
228
+ return
229
+ }
168
230
  }
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.28",
4
+ "version": "0.0.29",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "tsup-node ./lib/index.ts --format esm --dts",
@@ -0,0 +1,111 @@
1
+ import { expect, test } from "bun:test"
2
+ import { createCanvas } from "@napi-rs/canvas"
3
+ import type { PcbPlatedHole } from "circuit-json"
4
+ import { CircuitToCanvasDrawer } from "../../lib/drawer"
5
+
6
+ test("draw hole with polygon pad - circle hole", async () => {
7
+ const canvas = createCanvas(100, 100)
8
+ const ctx = canvas.getContext("2d")
9
+ const drawer = new CircuitToCanvasDrawer(ctx)
10
+
11
+ ctx.fillStyle = "#1a1a1a"
12
+ ctx.fillRect(0, 0, 100, 100)
13
+
14
+ const hole: PcbPlatedHole = {
15
+ type: "pcb_plated_hole",
16
+ pcb_plated_hole_id: "hole1",
17
+ shape: "hole_with_polygon_pad",
18
+ x: 50,
19
+ y: 50,
20
+ hole_shape: "circle",
21
+ hole_diameter: 15,
22
+ pad_outline: [
23
+ { x: -20, y: -20 },
24
+ { x: 20, y: -20 },
25
+ { x: 25, y: 0 },
26
+ { x: 20, y: 20 },
27
+ { x: -20, y: 20 },
28
+ { x: -25, y: 0 },
29
+ ],
30
+ layers: ["top", "bottom"],
31
+ } as any
32
+
33
+ drawer.drawElements([hole])
34
+
35
+ await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
36
+ import.meta.path,
37
+ "hole-with-polygon-pad-circle",
38
+ )
39
+ })
40
+
41
+ test("draw hole with polygon pad - pill hole with offset", async () => {
42
+ const canvas = createCanvas(100, 100)
43
+ const ctx = canvas.getContext("2d")
44
+ const drawer = new CircuitToCanvasDrawer(ctx)
45
+
46
+ ctx.fillStyle = "#1a1a1a"
47
+ ctx.fillRect(0, 0, 100, 100)
48
+
49
+ const hole: PcbPlatedHole = {
50
+ type: "pcb_plated_hole",
51
+ pcb_plated_hole_id: "hole1",
52
+ shape: "hole_with_polygon_pad",
53
+ x: 50,
54
+ y: 50,
55
+ hole_shape: "pill",
56
+ hole_width: 12,
57
+ hole_height: 8,
58
+ hole_offset_x: 5,
59
+ hole_offset_y: -3,
60
+ pad_outline: [
61
+ { x: -25, y: -15 },
62
+ { x: 25, y: -15 },
63
+ { x: 30, y: 0 },
64
+ { x: 25, y: 15 },
65
+ { x: -25, y: 15 },
66
+ { x: -30, y: 0 },
67
+ ],
68
+ layers: ["top", "bottom"],
69
+ } as any
70
+
71
+ drawer.drawElements([hole])
72
+
73
+ await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
74
+ import.meta.path,
75
+ "hole-with-polygon-pad-pill-offset",
76
+ )
77
+ })
78
+
79
+ test("draw hole with polygon pad - pill hole", async () => {
80
+ const canvas = createCanvas(100, 100)
81
+ const ctx = canvas.getContext("2d")
82
+ const drawer = new CircuitToCanvasDrawer(ctx)
83
+
84
+ ctx.fillStyle = "#1a1a1a"
85
+ ctx.fillRect(0, 0, 100, 100)
86
+
87
+ const hole: PcbPlatedHole = {
88
+ type: "pcb_plated_hole",
89
+ pcb_plated_hole_id: "hole1",
90
+ shape: "hole_with_polygon_pad",
91
+ x: 50,
92
+ y: 50,
93
+ hole_shape: "rotated_pill",
94
+ hole_width: 15,
95
+ hole_height: 10,
96
+ pad_outline: [
97
+ { x: -20, y: -20 },
98
+ { x: 20, y: -20 },
99
+ { x: 20, y: 20 },
100
+ { x: -20, y: 20 },
101
+ ],
102
+ layers: ["top", "bottom"],
103
+ } as any
104
+
105
+ drawer.drawElements([hole])
106
+
107
+ await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
108
+ import.meta.path,
109
+ "hole-with-polygon-pad-pill",
110
+ )
111
+ })