circuit-to-canvas 0.0.17 → 0.0.18

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 (25) hide show
  1. package/dist/index.d.ts +9 -7
  2. package/dist/index.js +16 -10
  3. package/lib/drawer/CircuitToCanvasDrawer.ts +3 -2
  4. package/lib/drawer/elements/pcb-copper-text.ts +2 -2
  5. package/lib/drawer/shapes/text/getAlphabetLayout.ts +41 -0
  6. package/lib/drawer/shapes/text/getTextStartPosition.ts +53 -0
  7. package/lib/drawer/shapes/text/index.ts +3 -0
  8. package/lib/drawer/shapes/{text.ts → text/text.ts} +5 -104
  9. package/package.json +2 -1
  10. package/tests/board-snapshot/__snapshots__/usb-c-flashlight-board.snap.png +0 -0
  11. package/tests/board-snapshot/usb-c-flashlight-board.test.ts +15 -0
  12. package/tests/board-snapshot/usb-c-flashlight.json +2456 -0
  13. package/tests/elements/__snapshots__/fabrication-note-text-descenders.snap.png +0 -0
  14. package/tests/elements/__snapshots__/fabrication-note-text-full-charset.snap.png +0 -0
  15. package/tests/elements/__snapshots__/pcb-fabrication-note-text-rgba-color.snap.png +0 -0
  16. package/tests/elements/__snapshots__/pcb-fabrication-note-text-small.snap.png +0 -0
  17. package/tests/elements/__snapshots__/pcb-note-text-anchor-alignment.snap.png +0 -0
  18. package/tests/elements/__snapshots__/pcb-note-text-custom-color.snap.png +0 -0
  19. package/tests/elements/__snapshots__/pcb-note-text-small.snap.png +0 -0
  20. package/tests/fixtures/assets/label-circuit-to-canvas.png +0 -0
  21. package/tests/fixtures/assets/label-circuit-to-svg.png +0 -0
  22. package/tests/fixtures/getStackedPngSvgComparison.ts +62 -0
  23. package/tests/fixtures/stackPngsVertically.ts +82 -0
  24. package/tests/shapes/__snapshots__/oval.snap.png +0 -0
  25. package/tsconfig.json +1 -0
@@ -0,0 +1,62 @@
1
+ import { createCanvas } from "@napi-rs/canvas"
2
+ import type { AnyCircuitElement } from "circuit-json"
3
+ import { convertCircuitJsonToPcbSvg } from "circuit-to-svg"
4
+ import type { Bounds } from "@tscircuit/math-utils"
5
+ import { CircuitToCanvasDrawer } from "../../lib/drawer"
6
+ import { stackPngsVertically, svgToPng } from "./stackPngsVertically"
7
+ import { getBoundsOfPcbElements } from "@tscircuit/circuit-json-util"
8
+
9
+ export interface StackedPngSvgComparisonOptions {
10
+ width?: number
11
+ height?: number
12
+ padding?: number
13
+ }
14
+
15
+ /**
16
+ * Generate a stacked PNG comparison of circuit-to-canvas vs circuit-to-svg rendering.
17
+ *
18
+ * @param circuitJson - Array of circuit elements to render
19
+ * @param options - Optional configuration for width, height, and padding
20
+ * @returns Promise<Buffer> - Stacked PNG with both renderings labeled
21
+ */
22
+ export async function getStackedPngSvgComparison(
23
+ circuitJson: AnyCircuitElement[],
24
+ options: StackedPngSvgComparisonOptions = {},
25
+ ): Promise<Buffer> {
26
+ const { width = 400, height = 800, padding = 4 } = options
27
+
28
+ const bounds = getBoundsOfPcbElements(circuitJson)
29
+
30
+ // Generate circuit-to-canvas PNG
31
+ const canvas = createCanvas(width, height)
32
+ const ctx = canvas.getContext("2d")
33
+ const drawer = new CircuitToCanvasDrawer(ctx)
34
+
35
+ ctx.fillStyle = "#000000"
36
+ ctx.fillRect(0, 0, width, height)
37
+
38
+ drawer.setCameraBounds({
39
+ minX: bounds.minX,
40
+ maxX: bounds.maxX,
41
+ minY: bounds.minY,
42
+ maxY: bounds.maxY,
43
+ })
44
+ drawer.drawElements(circuitJson)
45
+
46
+ const canvasPng = canvas.toBuffer("image/png")
47
+
48
+ // Generate circuit-to-svg PNG
49
+ const svg = convertCircuitJsonToPcbSvg(circuitJson, {
50
+ width,
51
+ height,
52
+ })
53
+ const svgPng = svgToPng(svg)
54
+
55
+ // Stack both PNGs vertically with labels
56
+ const stackedPng = await stackPngsVertically([
57
+ { png: canvasPng, label: "circuit-to-canvas" },
58
+ { png: svgPng, label: "circuit-to-svg" },
59
+ ])
60
+
61
+ return stackedPng
62
+ }
@@ -0,0 +1,82 @@
1
+ import { createCanvas, loadImage } from "@napi-rs/canvas"
2
+ import { Resvg } from "@resvg/resvg-js"
3
+ import * as fs from "node:fs"
4
+ import * as path from "node:path"
5
+
6
+ // Pre-generated label PNGs for common labels
7
+ const labelPngCache: Map<string, Buffer> = new Map()
8
+ const assetsDir = path.join(__dirname, "assets")
9
+
10
+ const getPreGeneratedLabelPng = (label: string): Buffer => {
11
+ const cacheKey = label
12
+ if (labelPngCache.has(cacheKey)) {
13
+ return labelPngCache.get(cacheKey)!
14
+ }
15
+
16
+ const filename = `label-${label}.png`
17
+ const filepath = path.join(assetsDir, filename)
18
+ if (fs.existsSync(filepath)) {
19
+ const png = fs.readFileSync(filepath)
20
+ labelPngCache.set(cacheKey, png)
21
+ return png
22
+ }
23
+ throw new Error(`Label PNG not found for label: ${label}`)
24
+ }
25
+
26
+ export const stackPngsVertically = async (
27
+ pngs: Array<{ png: Buffer; label: string }>,
28
+ ): Promise<Buffer> => {
29
+ if (pngs.length === 0) {
30
+ throw new Error("No PNGs provided to stack")
31
+ }
32
+
33
+ if (pngs.length === 1) {
34
+ return pngs[0]!.png
35
+ }
36
+
37
+ // Load all images to get dimensions
38
+ const images = await Promise.all(
39
+ pngs.map(async ({ png }) => await loadImage(png)),
40
+ )
41
+
42
+ // Calculate the maximum width and total height
43
+ const maxWidth = Math.max(...images.map((img) => img.width))
44
+ const totalHeight = images.reduce((sum, img) => sum + img.height, 0)
45
+
46
+ // Create the final canvas
47
+ const canvas = createCanvas(maxWidth, totalHeight)
48
+ const ctx = canvas.getContext("2d")
49
+
50
+ // Fill with dark background
51
+ ctx.fillStyle = "#1a1a1a"
52
+ ctx.fillRect(0, 0, maxWidth, totalHeight)
53
+
54
+ // Draw each image and its label
55
+ let currentY = 0
56
+ for (let i = 0; i < pngs.length; i++) {
57
+ const { label } = pngs[i]!
58
+ const img = images[i]!
59
+ const width = img.width
60
+ const height = img.height
61
+
62
+ // Center horizontally if image is narrower than max width
63
+ const left = Math.floor((maxWidth - width) / 2)
64
+
65
+ // Draw the image
66
+ ctx.drawImage(img, left, currentY)
67
+
68
+ // Draw the label
69
+ const labelPng = getPreGeneratedLabelPng(label)
70
+ const labelImg = await loadImage(labelPng!)
71
+ ctx.drawImage(labelImg, 0, currentY)
72
+
73
+ currentY += height
74
+ }
75
+
76
+ return canvas.toBuffer("image/png")
77
+ }
78
+
79
+ export const svgToPng = (svg: string): Buffer => {
80
+ const resvg = new Resvg(svg)
81
+ return resvg.render().asPng()
82
+ }
package/tsconfig.json CHANGED
@@ -7,6 +7,7 @@
7
7
  "moduleDetection": "force",
8
8
  "jsx": "react-jsx",
9
9
  "allowJs": true,
10
+ "resolveJsonModule": true,
10
11
 
11
12
  // Bundler mode
12
13
  "moduleResolution": "bundler",