circuit-to-svg 0.0.2 → 0.0.3
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/README.md +59 -1
- package/dist/index.js +77 -52
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# circuit-to-svg
|
|
2
|
+
|
|
3
|
+
A TypeScript library for converting circuit descriptions (soup) to SVG representations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This library provides functionality to convert circuit descriptions, referred to as "soup", into SVG (Scalable Vector Graphics) representations. It supports both schematic and PCB (Printed Circuit Board) layouts.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Convert schematic circuit descriptions to SVG
|
|
12
|
+
- Convert PCB layouts to SVG
|
|
13
|
+
- Support for various circuit elements:
|
|
14
|
+
- Components
|
|
15
|
+
- Traces
|
|
16
|
+
- Text labels
|
|
17
|
+
- Net labels
|
|
18
|
+
- PCB boards
|
|
19
|
+
- PCB components
|
|
20
|
+
- PCB traces
|
|
21
|
+
- PCB holes and pads
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @tscircuit/circuit-to-svg
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { soupToSvg, pcbSoupToSvg } from '@tscircuit/circuit-to-svg';
|
|
33
|
+
|
|
34
|
+
// For schematic circuits
|
|
35
|
+
const schematicSoup = [...]; // Your schematic circuit description
|
|
36
|
+
const schematicSvg = soupToSvg(schematicSoup);
|
|
37
|
+
|
|
38
|
+
// For PCB layouts
|
|
39
|
+
const pcbSoup = [...]; // Your PCB layout description
|
|
40
|
+
const pcbSvg = pcbSoupToSvg(pcbSoup);
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## API
|
|
44
|
+
|
|
45
|
+
### `soupToSvg(soup: AnySoupElement[]): string`
|
|
46
|
+
|
|
47
|
+
Converts a schematic circuit description to an SVG string.
|
|
48
|
+
|
|
49
|
+
### `pcbSoupToSvg(soup: AnySoupElement[]): string`
|
|
50
|
+
|
|
51
|
+
Converts a PCB layout description to an SVG string.
|
|
52
|
+
|
|
53
|
+
## Contributing
|
|
54
|
+
|
|
55
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
This project is licensed under the MIT License.
|
package/dist/index.js
CHANGED
|
@@ -26,59 +26,63 @@ __export(src_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(src_exports);
|
|
27
27
|
|
|
28
28
|
// src/lib/pcb-soup-to-svg.ts
|
|
29
|
+
var import_transformation_matrix = require("transformation-matrix");
|
|
29
30
|
function pcbSoupToSvg(soup) {
|
|
30
31
|
const svgContent = [];
|
|
31
32
|
let minX = Number.POSITIVE_INFINITY;
|
|
32
33
|
let minY = Number.POSITIVE_INFINITY;
|
|
33
34
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
34
35
|
let maxY = Number.NEGATIVE_INFINITY;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if ("
|
|
41
|
-
|
|
42
|
-
svgContent.push(createPcbComponent(component));
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
for (const trace of soup.filter((item) => item.type === "pcb_trace")) {
|
|
46
|
-
svgContent.push(createPcbTrace(trace));
|
|
47
|
-
if ("route" in trace) {
|
|
48
|
-
updateTraceBounds(trace.route);
|
|
36
|
+
for (const item of soup) {
|
|
37
|
+
if ("center" in item && "width" in item && "height" in item) {
|
|
38
|
+
updateBounds(item.center, item.width, item.height);
|
|
39
|
+
} else if ("x" in item && "y" in item) {
|
|
40
|
+
updateBounds({ x: item.x, y: item.y }, 0, 0);
|
|
41
|
+
} else if ("route" in item) {
|
|
42
|
+
updateTraceBounds(item.route);
|
|
49
43
|
}
|
|
50
44
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
45
|
+
const padding = 1;
|
|
46
|
+
const circuitWidth = maxX - minX + 2 * padding;
|
|
47
|
+
const circuitHeight = maxY - minY + 2 * padding;
|
|
48
|
+
const svgWidth = 800;
|
|
49
|
+
const svgHeight = 600;
|
|
50
|
+
const scaleX = svgWidth / circuitWidth;
|
|
51
|
+
const scaleY = svgHeight / circuitHeight;
|
|
52
|
+
const scaleFactor = Math.min(scaleX, scaleY);
|
|
53
|
+
const offsetX = (svgWidth - circuitWidth * scaleFactor) / 2;
|
|
54
|
+
const offsetY = (svgHeight - circuitHeight * scaleFactor) / 2;
|
|
55
|
+
const transform = (0, import_transformation_matrix.compose)(
|
|
56
|
+
(0, import_transformation_matrix.translate)(offsetX - minX * scaleFactor + padding * scaleFactor, svgHeight - offsetY + minY * scaleFactor - padding * scaleFactor),
|
|
57
|
+
(0, import_transformation_matrix.scale)(scaleFactor, -scaleFactor)
|
|
58
|
+
// Flip in y-direction
|
|
59
|
+
);
|
|
60
|
+
for (const item of soup) {
|
|
61
|
+
if (item.type === "pcb_component") {
|
|
62
|
+
svgContent.push(createPcbComponent(item, transform));
|
|
63
|
+
} else if (item.type === "pcb_trace") {
|
|
64
|
+
svgContent.push(createPcbTrace(item, transform));
|
|
65
|
+
} else if (item.type === "pcb_plated_hole" || item.type === "pcb_smtpad") {
|
|
66
|
+
svgContent.push(createPcbHole(item, transform));
|
|
57
67
|
}
|
|
58
68
|
}
|
|
59
|
-
const padding = 5;
|
|
60
|
-
const width = maxX - minX + 2 * padding;
|
|
61
|
-
const height = maxY - minY + 2 * padding;
|
|
62
|
-
const viewBox = `${minX - padding} ${-maxY - padding} ${width} ${height}`;
|
|
63
69
|
return `
|
|
64
|
-
|
|
70
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}">
|
|
65
71
|
<style>
|
|
66
72
|
.pcb-board { fill: #000; }
|
|
67
|
-
.pcb-trace { stroke: #
|
|
73
|
+
.pcb-trace { stroke: #FF0000; stroke-width: 0.3; fill: none; }
|
|
68
74
|
.pcb-hole { fill: #FF00FF; }
|
|
69
|
-
.pcb-pad { fill: #
|
|
70
|
-
.pcb-
|
|
75
|
+
.pcb-pad { fill: #FF0000; }
|
|
76
|
+
.pcb-boundary { fill: none; stroke: #FFFFFF; stroke-width: 0.5; }
|
|
71
77
|
</style>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const halfWidth = width2 / 2;
|
|
81
|
-
const halfHeight = height2 / 2;
|
|
78
|
+
<rect class="pcb-board" x="0" y="0" width="${svgWidth}" height="${svgHeight}" />
|
|
79
|
+
${svgContent.join("\n")}
|
|
80
|
+
${createPcbBoundary(transform, minX, minY, maxX, maxY)}
|
|
81
|
+
</svg>
|
|
82
|
+
`;
|
|
83
|
+
function updateBounds(center, width, height) {
|
|
84
|
+
const halfWidth = width / 2;
|
|
85
|
+
const halfHeight = height / 2;
|
|
82
86
|
minX = Math.min(minX, center.x - halfWidth);
|
|
83
87
|
minY = Math.min(minY, center.y - halfHeight);
|
|
84
88
|
maxX = Math.max(maxX, center.x + halfWidth);
|
|
@@ -93,27 +97,48 @@ function pcbSoupToSvg(soup) {
|
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
|
-
function createPcbComponent(component) {
|
|
100
|
+
function createPcbComponent(component, transform) {
|
|
97
101
|
const { center, width, height, rotation = 0 } = component;
|
|
98
|
-
const
|
|
102
|
+
const [x, y] = (0, import_transformation_matrix.applyToPoint)(transform, [center.x, center.y]);
|
|
103
|
+
const scaledWidth = width * Math.abs(transform.a);
|
|
104
|
+
const scaledHeight = height * Math.abs(transform.d);
|
|
105
|
+
const transformStr = `translate(${x}, ${y}) rotate(${-rotation}) scale(1, -1)`;
|
|
99
106
|
return `
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
<g transform="${transformStr}">
|
|
108
|
+
<rect class="pcb-component" x="${-scaledWidth / 2}" y="${-scaledHeight / 2}" width="${scaledWidth}" height="${scaledHeight}" />
|
|
109
|
+
<rect class="pcb-component-outline" x="${-scaledWidth / 2}" y="${-scaledHeight / 2}" width="${scaledWidth}" height="${scaledHeight}" />
|
|
110
|
+
</g>
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
function createPcbHole(hole, transform) {
|
|
114
|
+
const [x, y] = (0, import_transformation_matrix.applyToPoint)(transform, [hole.x, hole.y]);
|
|
115
|
+
if (hole.type === "pcb_plated_hole") {
|
|
116
|
+
const scaledRadius = hole.outer_diameter / 2 * Math.abs(transform.a);
|
|
117
|
+
return `<circle class="pcb-hole" cx="${x}" cy="${y}" r="${scaledRadius}" />`;
|
|
118
|
+
}
|
|
119
|
+
if (hole.type === "pcb_smtpad") {
|
|
120
|
+
const scaledWidth = hole.width * Math.abs(transform.a);
|
|
121
|
+
const scaledHeight = hole.height * Math.abs(transform.d);
|
|
122
|
+
return `<rect class="pcb-pad" x="${x - scaledWidth / 2}" y="${y - scaledHeight / 2}" width="${scaledWidth}" height="${scaledHeight}" />`;
|
|
123
|
+
}
|
|
124
|
+
return "";
|
|
105
125
|
}
|
|
106
|
-
function createPcbTrace(trace) {
|
|
126
|
+
function createPcbTrace(trace, transform) {
|
|
127
|
+
if (!trace.route || !Array.isArray(trace.route)) return "";
|
|
107
128
|
const path = trace.route.map((point, index) => {
|
|
108
|
-
|
|
129
|
+
const [x, y] = (0, import_transformation_matrix.applyToPoint)(transform, [point.x, point.y]);
|
|
130
|
+
return index === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
|
|
109
131
|
}).join(" ");
|
|
110
132
|
return `<path class="pcb-trace" d="${path}" />`;
|
|
111
133
|
}
|
|
112
|
-
function
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
134
|
+
function createPcbBoundary(transform, minX, minY, maxX, maxY) {
|
|
135
|
+
const [x1, y1] = (0, import_transformation_matrix.applyToPoint)(transform, [minX, minY]);
|
|
136
|
+
const [x2, y2] = (0, import_transformation_matrix.applyToPoint)(transform, [maxX, maxY]);
|
|
137
|
+
const width = Math.abs(x2 - x1);
|
|
138
|
+
const height = Math.abs(y2 - y1);
|
|
139
|
+
const x = Math.min(x1, x2);
|
|
140
|
+
const y = Math.min(y1, y2);
|
|
141
|
+
return `<rect class="pcb-boundary" x="${x}" y="${y}" width="${width}" height="${height}" />`;
|
|
117
142
|
}
|
|
118
143
|
|
|
119
144
|
// src/lib/soup-to-svg.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/lib/pcb-soup-to-svg.ts","../src/lib/soup-to-svg.ts"],"sourcesContent":["export * from \"./lib\";\n\n","import type { AnySoupElement } from \"@tscircuit/soup\";\n\nfunction pcbSoupToSvg(soup: AnySoupElement[]): string {\n const svgContent: string[] = [];\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n \n // Process PCB board\n const board = soup.find((item) => item.type === \"pcb_board\");\n if (board && \"center\" in board && \"width\" in board && \"height\" in board) {\n updateBounds(board.center, board.width, board.height);\n }\n \n // Process PCB components\n for (const component of soup.filter((item) => item.type === \"pcb_component\")) {\n if (\"center\" in component && \"width\" in component && \"height\" in component) {\n updateBounds((component as { center: { x: number; y: number }; width: number; height: number }).center, component.width, component.height);\n svgContent.push(createPcbComponent(component));\n }\n }\n \n // Process PCB traces\n for (const trace of soup.filter((item) => item.type === \"pcb_trace\")) {\n svgContent.push(createPcbTrace(trace));\n if ('route' in trace) {\n updateTraceBounds(trace.route);\n }\n }\n \n // Process PCB plated holes and SMT pads\n for (const item of soup.filter((i) => i.type === \"pcb_plated_hole\" || i.type === \"pcb_smtpad\")) {\n if ('x' in item && 'y' in item && ('outer_diameter' in item || ('width' in item && 'height' in item))) {\n const diameter = 'outer_diameter' in item ? item.outer_diameter : item.width;\n const height = 'outer_diameter' in item ? item.outer_diameter : item.height;\n updateBounds({ x: item.x, y: item.y }, diameter, height);\n svgContent.push(createPcbHole(item));\n }\n }\n \n const padding = 5;\n const width = maxX - minX + 2 * padding;\n const height = maxY - minY + 2 * padding;\n const viewBox = `${minX - padding} ${-maxY - padding} ${width} ${height}`;\n \n return `\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${viewBox}\" width=\"800\" height=\"600\">\n <style>\n .pcb-board { fill: #000; }\n .pcb-trace { stroke: #CC0000; stroke-width: 0.1; fill: none; }\n .pcb-hole { fill: #FF00FF; }\n .pcb-pad { fill: #CC0000; }\n .pcb-outline { fill: none; stroke: white; stroke-width: 0.1; }\n </style>\n <g transform=\"scale(1, -1)\">\n <rect class=\"pcb-board\" x=\"${minX - padding}\" y=\"${minY - padding}\" width=\"${width}\" height=\"${height}\" />\n ${svgContent.join(\"\\n\")}\n <rect class=\"pcb-outline\" x=\"${minX}\" y=\"${minY}\" width=\"${maxX - minX}\" height=\"${maxY - minY}\" />\n </g>\n </svg>\n `;\n \n function updateBounds(center: any, width: any, height: any) {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n minX = Math.min(minX, center.x - halfWidth);\n minY = Math.min(minY, center.y - halfHeight);\n maxX = Math.max(maxX, center.x + halfWidth);\n maxY = Math.max(maxY, center.y + halfHeight);\n }\n \n function updateTraceBounds(route: any[]) {\n for (const point of route) {\n minX = Math.min(minX, point.x);\n minY = Math.min(minY, point.y);\n maxX = Math.max(maxX, point.x);\n maxY = Math.max(maxY, point.y);\n }\n }\n }\n \n function createPcbComponent(component: any): string {\n const { center, width, height, rotation = 0 } = component;\n const transform = `translate(${center.x}, ${center.y}) rotate(${-rotation})`;\n return `\n <g transform=\"${transform}\">\n <rect class=\"pcb-component\" x=\"${-width/2}\" y=\"${-height/2}\" width=\"${width}\" height=\"${height}\" />\n <rect class=\"pcb-component-outline\" x=\"${-width/2}\" y=\"${-height/2}\" width=\"${width}\" height=\"${height}\" />\n </g>\n `;\n }\n \n function createPcbTrace(trace: any): string {\n const path = trace.route.map((point: any, index: number) => {\n return index === 0 ? `M ${point.x} ${point.y}` : `L ${point.x} ${point.y}`;\n }).join(\" \");\n return `<path class=\"pcb-trace\" d=\"${path}\" />`;\n }\n \n function createPcbHole(hole: any): string {\n if (hole.type === \"pcb_plated_hole\") {\n return `<circle class=\"pcb-hole\" cx=\"${hole.x}\" cy=\"${hole.y}\" r=\"${hole.outer_diameter / 2}\" />`;\n }\n return `<rect class=\"pcb-pad\" x=\"${hole.x - hole.width/2}\" y=\"${hole.y - hole.height/2}\" width=\"${hole.width}\" height=\"${hole.height}\" />`;\n }\n\n\nexport { pcbSoupToSvg };\n\n","import type { AnySoupElement } from \"@tscircuit/soup\";\n\nfunction soupToSvg(soup: AnySoupElement[]): string {\n const svgContent: string[] = [];\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n\n // Process components\n for (const component of soup.filter(\n (item) => item.type === \"schematic_component\"\n )) {\n const svg = createSchematicComponent(component);\n svgContent.push(svg);\n if (\n \"center\" in component &&\n \"size\" in component &&\n \"rotation\" in component\n ) {\n updateBounds(component.center, component.size, component.rotation);\n }\n }\n\n // Process traces\n for (const trace of soup.filter((item) => item.type === \"schematic_trace\")) {\n const svg = createSchematicTrace(trace);\n svgContent.push(svg);\n updateTraceBounds(trace.edges);\n }\n\n // Process text\n for (const text of soup.filter((item) => item.type === \"schematic_text\")) {\n const svg = createSchematicText(text);\n svgContent.push(svg);\n if (\"position\" in text) {\n updateTextBounds(text.position);\n }\n }\n\n // Process net labels\n for (const label of soup.filter(\n (item) => item.type === \"schematic_net_label\"\n )) {\n const svg = createSchematicNetLabel(label);\n svgContent.push(svg);\n const width = label.text.length * 0.15 + 0.3;\n const height = 0.3;\n minX = Math.min(minX, label.center.x);\n minY = Math.min(minY, label.center.y - height / 2);\n maxX = Math.max(maxX, label.center.x + width);\n maxY = Math.max(maxY, label.center.y + height / 2);\n }\n\n const padding = 1;\n const width = maxX - minX + 2 * padding;\n const height = maxY - minY + 2 * padding;\n const viewBox = `${minX - padding} ${minY - padding} ${width} ${height}`;\n\n return `\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${viewBox}\" width=\"1200\" height=\"600\">\n <style>\n .component { fill: none; stroke: red; stroke-width: 0.05; }\n .component-pin { fill: none; stroke: blue; stroke-width: 0.05; }\n .trace { stroke: green; stroke-width: 0.05; fill: none; }\n .text { font-family: Arial, sans-serif; font-size: 0.2px; }\n .net-label { font-family: Arial, sans-serif; font-size: 0.2px; fill: gray; }\n </style>\n ${svgContent.join(\"\\n\")}\n </svg>\n `;\n\n function updateBounds(center: any, size: any, rotation: number) {\n const corners = [\n { x: -size.width / 2, y: -size.height / 2 },\n { x: size.width / 2, y: -size.height / 2 },\n { x: size.width / 2, y: size.height / 2 },\n { x: -size.width / 2, y: size.height / 2 },\n ];\n\n for (const corner of corners) {\n const rotatedX =\n corner.x * Math.cos(rotation) -\n corner.y * Math.sin(rotation) +\n center.x;\n const rotatedY =\n corner.x * Math.sin(rotation) +\n corner.y * Math.cos(rotation) +\n center.y;\n minX = Math.min(minX, rotatedX);\n minY = Math.min(minY, rotatedY);\n maxX = Math.max(maxX, rotatedX);\n maxY = Math.max(maxY, rotatedY);\n }\n }\n\n function updateTraceBounds(edges: any[]) {\n for (const edge of edges) {\n minX = Math.min(minX, edge.from.x, edge.to.x);\n minY = Math.min(minY, edge.from.y, edge.to.y);\n maxX = Math.max(maxX, edge.from.x, edge.to.x);\n maxY = Math.max(maxY, edge.from.y, edge.to.y);\n }\n }\n\n function updateTextBounds(position: { x: number; y: number }) {\n minX = Math.min(minX, position.x);\n minY = Math.min(minY, position.y);\n maxX = Math.max(maxX, position.x);\n maxY = Math.max(maxY, position.y);\n }\n}\n\nfunction createSchematicComponent(component: any): string {\n const { center, size, rotation } = component;\n const transform = `translate(${center.x}, ${center.y}) rotate(${\n (rotation * 180) / Math.PI\n })`;\n const pinSize = 0.2; // Size of the square pins\n\n return `\n <g transform=\"${transform}\">\n <rect \n class=\"component\" \n x=\"${-size.width / 2}\" \n y=\"${-size.height / 2}\" \n width=\"${size.width}\" \n height=\"${size.height}\" \n />\n <rect \n class=\"component-pin\"\n x=\"${-size.width / 2 - pinSize / 2}\" \n y=\"${-pinSize / 2}\" \n width=\"${pinSize}\" \n height=\"${pinSize}\" \n />\n <rect \n class=\"component-pin\"\n x=\"${size.width / 2 - pinSize / 2}\" \n y=\"${-pinSize / 2}\" \n width=\"${pinSize}\" \n height=\"${pinSize}\" \n />\n </g>\n `;\n}\n\nfunction createSchematicTrace(trace: any): string {\n const path = trace.edges.map((edge: any, index: number) => {\n const fromPoint = `${edge.from.x} ${edge.from.y}`;\n const toPoint = `${edge.to.x} ${edge.to.y}`;\n \n if (index === 0) {\n return `M ${fromPoint} L ${toPoint}`;\n }\n // Check if this is a 90-degree turn\n const prevEdge = trace.edges[index - 1];\n if (prevEdge.to.x === edge.from.x && prevEdge.to.y === edge.from.y) {\n return `L ${toPoint}`;\n }\n // Insert a move command for discontinuous segments\n return `M ${fromPoint} L ${toPoint}`;\n }).join(' ');\n\n return `<path class=\"trace\" d=\"${path}\" />`;\n}\n\nfunction createSchematicText(text: any): string {\n return `\n <text \n class=\"text\" \n x=\"${text.position.x}\" \n y=\"${text.position.y}\" \n text-anchor=\"${getTextAnchor(text.anchor)}\"\n >${text.text}</text>\n `;\n}\n\nfunction createSchematicNetLabel(label: any): string {\n const width = label.text.length * 0.15 + 0.3;\n const height = 0.3;\n const arrowTip = 0.15;\n const isLeftAnchor = label.anchor_side === \"left\";\n\n // Move the entire label to the left\n const labelCenterX = isLeftAnchor ? label.center.x - width / 3 - 0.2 : label.center.x;\n\n const path = isLeftAnchor\n ? `\n M ${labelCenterX + width},${label.center.y - height / 2}\n L ${labelCenterX + arrowTip},${label.center.y - height / 2}\n L ${labelCenterX},${label.center.y}\n L ${labelCenterX + arrowTip},${label.center.y + height / 2}\n L ${labelCenterX + width},${label.center.y + height / 2}\n Z\n `\n : `\n M ${labelCenterX},${label.center.y - height / 2}\n L ${labelCenterX + width - arrowTip},${label.center.y - height / 2}\n L ${labelCenterX + width},${label.center.y}\n L ${labelCenterX + width - arrowTip},${label.center.y + height / 2}\n L ${labelCenterX},${label.center.y + height / 2}\n Z\n `;\n\n // Keep text centered within the label\n const textX = labelCenterX + width / 2;\n\n // Add a line to connect the label to the resistor\n const connectingLine = `\n <line \n x1=\"${labelCenterX + width}\" \n y1=\"${label.center.y}\" \n x2=\"${label.center.x}\" \n y2=\"${label.center.y}\" \n stroke=\"green\" \n stroke-width=\"0.05\"\n />\n `;\n\n return `\n <g class=\"net-label\">\n ${connectingLine}\n <path d=\"${path}\" fill=\"white\" stroke=\"black\" stroke-width=\"0.02\"/>\n <text \n x=\"${textX}\" \n y=\"${label.center.y}\" \n text-anchor=\"middle\" \n dominant-baseline=\"central\"\n fill=\"black\"\n >${label.text}</text>\n </g>\n `;\n}\n\nfunction getTextAnchor(anchor: string): string {\n switch (anchor) {\n case \"left\":\n return \"start\";\n case \"right\":\n return \"end\";\n default:\n return \"middle\";\n }\n}\n\nexport { soupToSvg };\n"],"mappings":"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","names":["height","width","width","height"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lib/pcb-soup-to-svg.ts","../src/lib/soup-to-svg.ts"],"sourcesContent":["export * from \"./lib\";\n\n","import type { AnySoupElement } from \"@tscircuit/soup\";\nimport { applyToPoint, compose, scale, translate } from \"transformation-matrix\";\n\nfunction pcbSoupToSvg(soup: AnySoupElement[]): string {\n const svgContent: string[] = [];\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n\n // Process all elements to determine bounds\n for (const item of soup) {\n if (\"center\" in item && \"width\" in item && \"height\" in item) {\n updateBounds(item.center, item.width, item.height);\n } else if (\"x\" in item && \"y\" in item) {\n updateBounds({ x: item.x, y: item.y }, 0, 0);\n } else if (\"route\" in item) {\n updateTraceBounds(item.route);\n }\n }\n\n const padding = 1; // Reduced padding for tighter boundary\n const circuitWidth = maxX - minX + 2 * padding;\n const circuitHeight = maxY - minY + 2 * padding;\n\n const svgWidth = 800;\n const svgHeight = 600;\n\n // Calculate scale factor to fit the circuit within the SVG, maintaining aspect ratio\n const scaleX = svgWidth / circuitWidth;\n const scaleY = svgHeight / circuitHeight;\n const scaleFactor = Math.min(scaleX, scaleY);\n\n // Calculate centering offsets\n const offsetX = (svgWidth - circuitWidth * scaleFactor) / 2;\n const offsetY = (svgHeight - circuitHeight * scaleFactor) / 2;\n\n const transform = compose(\n translate(offsetX - minX * scaleFactor + padding * scaleFactor, svgHeight - offsetY + minY * scaleFactor - padding * scaleFactor),\n scale(scaleFactor, -scaleFactor) // Flip in y-direction\n );\n\n // Process PCB elements\n for (const item of soup) {\n if (item.type === \"pcb_component\") {\n svgContent.push(createPcbComponent(item, transform));\n } else if (item.type === \"pcb_trace\") {\n svgContent.push(createPcbTrace(item, transform));\n } else if (item.type === \"pcb_plated_hole\" || item.type === \"pcb_smtpad\") {\n svgContent.push(createPcbHole(item, transform));\n }\n }\n\n return `\n <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${svgWidth}\" height=\"${svgHeight}\">\n <style>\n .pcb-board { fill: #000; }\n .pcb-trace { stroke: #FF0000; stroke-width: 0.3; fill: none; }\n .pcb-hole { fill: #FF00FF; }\n .pcb-pad { fill: #FF0000; }\n .pcb-boundary { fill: none; stroke: #FFFFFF; stroke-width: 0.5; }\n </style>\n <rect class=\"pcb-board\" x=\"0\" y=\"0\" width=\"${svgWidth}\" height=\"${svgHeight}\" />\n ${svgContent.join(\"\\n\")}\n ${createPcbBoundary(transform, minX, minY, maxX, maxY)}\n </svg>\n `;\n\n function updateBounds(center: any, width: any, height: any) {\n const halfWidth = width / 2;\n const halfHeight = height / 2;\n minX = Math.min(minX, center.x - halfWidth);\n minY = Math.min(minY, center.y - halfHeight);\n maxX = Math.max(maxX, center.x + halfWidth);\n maxY = Math.max(maxY, center.y + halfHeight);\n }\n\n function updateTraceBounds(route: any[]) {\n for (const point of route) {\n minX = Math.min(minX, point.x);\n minY = Math.min(minY, point.y);\n maxX = Math.max(maxX, point.x);\n maxY = Math.max(maxY, point.y);\n }\n }\n}\n\nfunction createPcbComponent(component: any, transform: any): string {\n const { center, width, height, rotation = 0 } = component;\n const [x, y] = applyToPoint(transform, [center.x, center.y]);\n const scaledWidth = width * Math.abs(transform.a);\n const scaledHeight = height * Math.abs(transform.d);\n const transformStr = `translate(${x}, ${y}) rotate(${-rotation}) scale(1, -1)`; // Note the scale(1, -1) to flip the component\n return `\n <g transform=\"${transformStr}\">\n <rect class=\"pcb-component\" x=\"${-scaledWidth / 2}\" y=\"${-scaledHeight / 2}\" width=\"${scaledWidth}\" height=\"${scaledHeight}\" />\n <rect class=\"pcb-component-outline\" x=\"${-scaledWidth / 2}\" y=\"${-scaledHeight / 2}\" width=\"${scaledWidth}\" height=\"${scaledHeight}\" />\n </g>\n `;\n}\n\nfunction createPcbHole(hole: any, transform: any): string {\n const [x, y] = applyToPoint(transform, [hole.x, hole.y]);\n if (hole.type === \"pcb_plated_hole\") {\n const scaledRadius = (hole.outer_diameter / 2) * Math.abs(transform.a);\n return `<circle class=\"pcb-hole\" cx=\"${x}\" cy=\"${y}\" r=\"${scaledRadius}\" />`;\n }\n if (hole.type === \"pcb_smtpad\") {\n const scaledWidth = hole.width * Math.abs(transform.a);\n const scaledHeight = hole.height * Math.abs(transform.d);\n return `<rect class=\"pcb-pad\" x=\"${x - scaledWidth / 2}\" y=\"${y - scaledHeight / 2}\" width=\"${scaledWidth}\" height=\"${scaledHeight}\" />`;\n }\n return \"\";\n}\n\nfunction createPcbTrace(trace: any, transform: any): string {\n if (!trace.route || !Array.isArray(trace.route)) return \"\";\n const path = trace.route\n .map((point: any, index: number) => {\n const [x, y] = applyToPoint(transform, [point.x, point.y]);\n return index === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;\n })\n .join(\" \");\n return `<path class=\"pcb-trace\" d=\"${path}\" />`;\n}\n\nfunction createPcbBoundary(transform: any, minX: number, minY: number, maxX: number, maxY: number): string {\n const [x1, y1] = applyToPoint(transform, [minX, minY]);\n const [x2, y2] = applyToPoint(transform, [maxX, maxY]);\n const width = Math.abs(x2 - x1);\n const height = Math.abs(y2 - y1);\n const x = Math.min(x1, x2);\n const y = Math.min(y1, y2);\n return `<rect class=\"pcb-boundary\" x=\"${x}\" y=\"${y}\" width=\"${width}\" height=\"${height}\" />`;\n}\n\nexport { pcbSoupToSvg };\n","import type { AnySoupElement } from \"@tscircuit/soup\";\n\nfunction soupToSvg(soup: AnySoupElement[]): string {\n const svgContent: string[] = [];\n let minX = Number.POSITIVE_INFINITY;\n let minY = Number.POSITIVE_INFINITY;\n let maxX = Number.NEGATIVE_INFINITY;\n let maxY = Number.NEGATIVE_INFINITY;\n\n // Process components\n for (const component of soup.filter(\n (item) => item.type === \"schematic_component\"\n )) {\n const svg = createSchematicComponent(component);\n svgContent.push(svg);\n if (\n \"center\" in component &&\n \"size\" in component &&\n \"rotation\" in component\n ) {\n updateBounds(component.center, component.size, component.rotation);\n }\n }\n\n // Process traces\n for (const trace of soup.filter((item) => item.type === \"schematic_trace\")) {\n const svg = createSchematicTrace(trace);\n svgContent.push(svg);\n updateTraceBounds(trace.edges);\n }\n\n // Process text\n for (const text of soup.filter((item) => item.type === \"schematic_text\")) {\n const svg = createSchematicText(text);\n svgContent.push(svg);\n if (\"position\" in text) {\n updateTextBounds(text.position);\n }\n }\n\n // Process net labels\n for (const label of soup.filter(\n (item) => item.type === \"schematic_net_label\"\n )) {\n const svg = createSchematicNetLabel(label);\n svgContent.push(svg);\n const width = label.text.length * 0.15 + 0.3;\n const height = 0.3;\n minX = Math.min(minX, label.center.x);\n minY = Math.min(minY, label.center.y - height / 2);\n maxX = Math.max(maxX, label.center.x + width);\n maxY = Math.max(maxY, label.center.y + height / 2);\n }\n\n const padding = 1;\n const width = maxX - minX + 2 * padding;\n const height = maxY - minY + 2 * padding;\n const viewBox = `${minX - padding} ${minY - padding} ${width} ${height}`;\n\n return `\n <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"${viewBox}\" width=\"1200\" height=\"600\">\n <style>\n .component { fill: none; stroke: red; stroke-width: 0.05; }\n .component-pin { fill: none; stroke: blue; stroke-width: 0.05; }\n .trace { stroke: green; stroke-width: 0.05; fill: none; }\n .text { font-family: Arial, sans-serif; font-size: 0.2px; }\n .net-label { font-family: Arial, sans-serif; font-size: 0.2px; fill: gray; }\n </style>\n ${svgContent.join(\"\\n\")}\n </svg>\n `;\n\n function updateBounds(center: any, size: any, rotation: number) {\n const corners = [\n { x: -size.width / 2, y: -size.height / 2 },\n { x: size.width / 2, y: -size.height / 2 },\n { x: size.width / 2, y: size.height / 2 },\n { x: -size.width / 2, y: size.height / 2 },\n ];\n\n for (const corner of corners) {\n const rotatedX =\n corner.x * Math.cos(rotation) -\n corner.y * Math.sin(rotation) +\n center.x;\n const rotatedY =\n corner.x * Math.sin(rotation) +\n corner.y * Math.cos(rotation) +\n center.y;\n minX = Math.min(minX, rotatedX);\n minY = Math.min(minY, rotatedY);\n maxX = Math.max(maxX, rotatedX);\n maxY = Math.max(maxY, rotatedY);\n }\n }\n\n function updateTraceBounds(edges: any[]) {\n for (const edge of edges) {\n minX = Math.min(minX, edge.from.x, edge.to.x);\n minY = Math.min(minY, edge.from.y, edge.to.y);\n maxX = Math.max(maxX, edge.from.x, edge.to.x);\n maxY = Math.max(maxY, edge.from.y, edge.to.y);\n }\n }\n\n function updateTextBounds(position: { x: number; y: number }) {\n minX = Math.min(minX, position.x);\n minY = Math.min(minY, position.y);\n maxX = Math.max(maxX, position.x);\n maxY = Math.max(maxY, position.y);\n }\n}\n\nfunction createSchematicComponent(component: any): string {\n const { center, size, rotation } = component;\n const transform = `translate(${center.x}, ${center.y}) rotate(${\n (rotation * 180) / Math.PI\n })`;\n const pinSize = 0.2; // Size of the square pins\n\n return `\n <g transform=\"${transform}\">\n <rect \n class=\"component\" \n x=\"${-size.width / 2}\" \n y=\"${-size.height / 2}\" \n width=\"${size.width}\" \n height=\"${size.height}\" \n />\n <rect \n class=\"component-pin\"\n x=\"${-size.width / 2 - pinSize / 2}\" \n y=\"${-pinSize / 2}\" \n width=\"${pinSize}\" \n height=\"${pinSize}\" \n />\n <rect \n class=\"component-pin\"\n x=\"${size.width / 2 - pinSize / 2}\" \n y=\"${-pinSize / 2}\" \n width=\"${pinSize}\" \n height=\"${pinSize}\" \n />\n </g>\n `;\n}\n\nfunction createSchematicTrace(trace: any): string {\n const path = trace.edges.map((edge: any, index: number) => {\n const fromPoint = `${edge.from.x} ${edge.from.y}`;\n const toPoint = `${edge.to.x} ${edge.to.y}`;\n \n if (index === 0) {\n return `M ${fromPoint} L ${toPoint}`;\n }\n // Check if this is a 90-degree turn\n const prevEdge = trace.edges[index - 1];\n if (prevEdge.to.x === edge.from.x && prevEdge.to.y === edge.from.y) {\n return `L ${toPoint}`;\n }\n // Insert a move command for discontinuous segments\n return `M ${fromPoint} L ${toPoint}`;\n }).join(' ');\n\n return `<path class=\"trace\" d=\"${path}\" />`;\n}\n\nfunction createSchematicText(text: any): string {\n return `\n <text \n class=\"text\" \n x=\"${text.position.x}\" \n y=\"${text.position.y}\" \n text-anchor=\"${getTextAnchor(text.anchor)}\"\n >${text.text}</text>\n `;\n}\n\nfunction createSchematicNetLabel(label: any): string {\n const width = label.text.length * 0.15 + 0.3;\n const height = 0.3;\n const arrowTip = 0.15;\n const isLeftAnchor = label.anchor_side === \"left\";\n\n // Move the entire label to the left\n const labelCenterX = isLeftAnchor ? label.center.x - width / 3 - 0.2 : label.center.x;\n\n const path = isLeftAnchor\n ? `\n M ${labelCenterX + width},${label.center.y - height / 2}\n L ${labelCenterX + arrowTip},${label.center.y - height / 2}\n L ${labelCenterX},${label.center.y}\n L ${labelCenterX + arrowTip},${label.center.y + height / 2}\n L ${labelCenterX + width},${label.center.y + height / 2}\n Z\n `\n : `\n M ${labelCenterX},${label.center.y - height / 2}\n L ${labelCenterX + width - arrowTip},${label.center.y - height / 2}\n L ${labelCenterX + width},${label.center.y}\n L ${labelCenterX + width - arrowTip},${label.center.y + height / 2}\n L ${labelCenterX},${label.center.y + height / 2}\n Z\n `;\n\n // Keep text centered within the label\n const textX = labelCenterX + width / 2;\n\n // Add a line to connect the label to the resistor\n const connectingLine = `\n <line \n x1=\"${labelCenterX + width}\" \n y1=\"${label.center.y}\" \n x2=\"${label.center.x}\" \n y2=\"${label.center.y}\" \n stroke=\"green\" \n stroke-width=\"0.05\"\n />\n `;\n\n return `\n <g class=\"net-label\">\n ${connectingLine}\n <path d=\"${path}\" fill=\"white\" stroke=\"black\" stroke-width=\"0.02\"/>\n <text \n x=\"${textX}\" \n y=\"${label.center.y}\" \n text-anchor=\"middle\" \n dominant-baseline=\"central\"\n fill=\"black\"\n >${label.text}</text>\n </g>\n `;\n}\n\nfunction getTextAnchor(anchor: string): string {\n switch (anchor) {\n case \"left\":\n return \"start\";\n case \"right\":\n return \"end\";\n default:\n return \"middle\";\n }\n}\n\nexport { soupToSvg };\n"],"mappings":"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","names":["width","height"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circuit-to-svg",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Convert Circuit JSON to SVG",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -31,8 +31,11 @@
|
|
|
31
31
|
"tsup": "^8.0.2",
|
|
32
32
|
"typescript": "^5.4.5"
|
|
33
33
|
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@tscircuit/soup": "^0.0.47"
|
|
36
|
+
},
|
|
34
37
|
"dependencies": {
|
|
35
38
|
"@tscircuit/routing": "^1.3.5",
|
|
36
|
-
"
|
|
39
|
+
"transformation-matrix": "^2.16.1"
|
|
37
40
|
}
|
|
38
41
|
}
|