circuit-to-svg 0.0.3 → 0.0.5
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.d.ts +3 -3
- package/dist/index.js +347 -194
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AnySoupElement } from '@tscircuit/soup';
|
|
2
2
|
|
|
3
|
-
declare function
|
|
3
|
+
declare function circuitJsonToPcbSvg(soup: AnySoupElement[]): string;
|
|
4
4
|
|
|
5
|
-
declare function
|
|
5
|
+
declare function circuitJsonToSchematicSvg(soup: AnySoupElement[]): string;
|
|
6
6
|
|
|
7
|
-
export {
|
|
7
|
+
export { circuitJsonToPcbSvg, circuitJsonToSchematicSvg };
|
package/dist/index.js
CHANGED
|
@@ -20,15 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
circuitJsonToPcbSvg: () => circuitJsonToPcbSvg,
|
|
24
|
+
circuitJsonToSchematicSvg: () => circuitJsonToSchematicSvg
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(src_exports);
|
|
27
27
|
|
|
28
28
|
// src/lib/pcb-soup-to-svg.ts
|
|
29
|
+
var import_svgson = require("svgson");
|
|
29
30
|
var import_transformation_matrix = require("transformation-matrix");
|
|
30
|
-
function
|
|
31
|
-
const svgContent = [];
|
|
31
|
+
function circuitJsonToPcbSvg(soup) {
|
|
32
32
|
let minX = Number.POSITIVE_INFINITY;
|
|
33
33
|
let minY = Number.POSITIVE_INFINITY;
|
|
34
34
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
@@ -57,29 +57,61 @@ function pcbSoupToSvg(soup) {
|
|
|
57
57
|
(0, import_transformation_matrix.scale)(scaleFactor, -scaleFactor)
|
|
58
58
|
// Flip in y-direction
|
|
59
59
|
);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
svgContent.push(createPcbTrace(item, transform));
|
|
65
|
-
} else if (item.type === "pcb_plated_hole" || item.type === "pcb_smtpad") {
|
|
66
|
-
svgContent.push(createPcbHole(item, transform));
|
|
60
|
+
const svgElements = soup.map((item) => {
|
|
61
|
+
const element = createSvgElement(item, transform);
|
|
62
|
+
if (element === null) {
|
|
63
|
+
console.warn("Null element created for item:", item);
|
|
67
64
|
}
|
|
65
|
+
return element;
|
|
66
|
+
}).filter((element) => element !== null);
|
|
67
|
+
const svgObject = {
|
|
68
|
+
name: "svg",
|
|
69
|
+
type: "element",
|
|
70
|
+
attributes: {
|
|
71
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
72
|
+
width: svgWidth.toString(),
|
|
73
|
+
height: svgHeight.toString()
|
|
74
|
+
},
|
|
75
|
+
children: [
|
|
76
|
+
{
|
|
77
|
+
name: "style",
|
|
78
|
+
type: "element",
|
|
79
|
+
children: [
|
|
80
|
+
{
|
|
81
|
+
type: "text",
|
|
82
|
+
value: `
|
|
83
|
+
.pcb-board { fill: #000; }
|
|
84
|
+
.pcb-trace { stroke: #FF0000; stroke-width: 0.3; fill: none; }
|
|
85
|
+
.pcb-hole { fill: #FF00FF; }
|
|
86
|
+
.pcb-pad { fill: #FF0000; }
|
|
87
|
+
.pcb-boundary { fill: none; stroke: #FFFFFF; stroke-width: 0.5; }
|
|
88
|
+
`
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "rect",
|
|
94
|
+
type: "element",
|
|
95
|
+
attributes: {
|
|
96
|
+
class: "pcb-board",
|
|
97
|
+
x: "0",
|
|
98
|
+
y: "0",
|
|
99
|
+
width: svgWidth.toString(),
|
|
100
|
+
height: svgHeight.toString()
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
...svgElements,
|
|
104
|
+
createPcbBoundary(transform, minX, minY, maxX, maxY)
|
|
105
|
+
].filter((child) => child !== null)
|
|
106
|
+
};
|
|
107
|
+
console.log("SVG Object:", JSON.stringify(svgObject, null, 2));
|
|
108
|
+
try {
|
|
109
|
+
return (0, import_svgson.stringify)(svgObject);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("Error stringifying SVG object:", error);
|
|
112
|
+
console.log("Problematic SVG object:", JSON.stringify(svgObject, null, 2));
|
|
113
|
+
throw error;
|
|
68
114
|
}
|
|
69
|
-
return `
|
|
70
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}">
|
|
71
|
-
<style>
|
|
72
|
-
.pcb-board { fill: #000; }
|
|
73
|
-
.pcb-trace { stroke: #FF0000; stroke-width: 0.3; fill: none; }
|
|
74
|
-
.pcb-hole { fill: #FF00FF; }
|
|
75
|
-
.pcb-pad { fill: #FF0000; }
|
|
76
|
-
.pcb-boundary { fill: none; stroke: #FFFFFF; stroke-width: 0.5; }
|
|
77
|
-
</style>
|
|
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
115
|
function updateBounds(center, width, height) {
|
|
84
116
|
const halfWidth = width / 2;
|
|
85
117
|
const halfHeight = height / 2;
|
|
@@ -97,39 +129,100 @@ function pcbSoupToSvg(soup) {
|
|
|
97
129
|
}
|
|
98
130
|
}
|
|
99
131
|
}
|
|
132
|
+
function createSvgElement(item, transform) {
|
|
133
|
+
switch (item.type) {
|
|
134
|
+
case "pcb_component":
|
|
135
|
+
return createPcbComponent(item, transform);
|
|
136
|
+
case "pcb_trace":
|
|
137
|
+
return createPcbTrace(item, transform);
|
|
138
|
+
case "pcb_plated_hole":
|
|
139
|
+
return createPcbHole(item, transform);
|
|
140
|
+
case "pcb_smtpad":
|
|
141
|
+
return createPcbSMTPad(item, transform);
|
|
142
|
+
default:
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
100
146
|
function createPcbComponent(component, transform) {
|
|
101
147
|
const { center, width, height, rotation = 0 } = component;
|
|
102
148
|
const [x, y] = (0, import_transformation_matrix.applyToPoint)(transform, [center.x, center.y]);
|
|
103
149
|
const scaledWidth = width * Math.abs(transform.a);
|
|
104
150
|
const scaledHeight = height * Math.abs(transform.d);
|
|
105
151
|
const transformStr = `translate(${x}, ${y}) rotate(${-rotation}) scale(1, -1)`;
|
|
106
|
-
return
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
152
|
+
return {
|
|
153
|
+
name: "g",
|
|
154
|
+
type: "element",
|
|
155
|
+
attributes: { transform: transformStr },
|
|
156
|
+
children: [
|
|
157
|
+
{
|
|
158
|
+
name: "rect",
|
|
159
|
+
type: "element",
|
|
160
|
+
attributes: {
|
|
161
|
+
class: "pcb-component",
|
|
162
|
+
x: (-scaledWidth / 2).toString(),
|
|
163
|
+
y: (-scaledHeight / 2).toString(),
|
|
164
|
+
width: scaledWidth.toString(),
|
|
165
|
+
height: scaledHeight.toString()
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: "rect",
|
|
170
|
+
type: "element",
|
|
171
|
+
attributes: {
|
|
172
|
+
class: "pcb-component-outline",
|
|
173
|
+
x: (-scaledWidth / 2).toString(),
|
|
174
|
+
y: (-scaledHeight / 2).toString(),
|
|
175
|
+
width: scaledWidth.toString(),
|
|
176
|
+
height: scaledHeight.toString()
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
};
|
|
112
181
|
}
|
|
113
182
|
function createPcbHole(hole, transform) {
|
|
114
183
|
const [x, y] = (0, import_transformation_matrix.applyToPoint)(transform, [hole.x, hole.y]);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
184
|
+
const scaledRadius = hole.outer_diameter / 2 * Math.abs(transform.a);
|
|
185
|
+
return {
|
|
186
|
+
name: "circle",
|
|
187
|
+
type: "element",
|
|
188
|
+
attributes: {
|
|
189
|
+
class: "pcb-hole",
|
|
190
|
+
cx: x.toString(),
|
|
191
|
+
cy: y.toString(),
|
|
192
|
+
r: scaledRadius.toString()
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function createPcbSMTPad(pad, transform) {
|
|
197
|
+
const [x, y] = (0, import_transformation_matrix.applyToPoint)(transform, [pad.x, pad.y]);
|
|
198
|
+
const width = pad.width * Math.abs(transform.a);
|
|
199
|
+
const height = pad.height * Math.abs(transform.d);
|
|
200
|
+
return {
|
|
201
|
+
name: "rect",
|
|
202
|
+
type: "element",
|
|
203
|
+
attributes: {
|
|
204
|
+
class: "pcb-pad",
|
|
205
|
+
x: (x - width / 2).toString(),
|
|
206
|
+
y: (y - height / 2).toString(),
|
|
207
|
+
width: width.toString(),
|
|
208
|
+
height: height.toString()
|
|
209
|
+
}
|
|
210
|
+
};
|
|
125
211
|
}
|
|
126
212
|
function createPcbTrace(trace, transform) {
|
|
127
|
-
if (!trace.route || !Array.isArray(trace.route)) return
|
|
213
|
+
if (!trace.route || !Array.isArray(trace.route)) return null;
|
|
128
214
|
const path = trace.route.map((point, index) => {
|
|
129
215
|
const [x, y] = (0, import_transformation_matrix.applyToPoint)(transform, [point.x, point.y]);
|
|
130
216
|
return index === 0 ? `M ${x} ${y}` : `L ${x} ${y}`;
|
|
131
217
|
}).join(" ");
|
|
132
|
-
return
|
|
218
|
+
return {
|
|
219
|
+
name: "path",
|
|
220
|
+
type: "element",
|
|
221
|
+
attributes: {
|
|
222
|
+
class: "pcb-trace",
|
|
223
|
+
d: path
|
|
224
|
+
}
|
|
225
|
+
};
|
|
133
226
|
}
|
|
134
227
|
function createPcbBoundary(transform, minX, minY, maxX, maxY) {
|
|
135
228
|
const [x1, y1] = (0, import_transformation_matrix.applyToPoint)(transform, [minX, minY]);
|
|
@@ -138,65 +231,113 @@ function createPcbBoundary(transform, minX, minY, maxX, maxY) {
|
|
|
138
231
|
const height = Math.abs(y2 - y1);
|
|
139
232
|
const x = Math.min(x1, x2);
|
|
140
233
|
const y = Math.min(y1, y2);
|
|
141
|
-
return
|
|
234
|
+
return {
|
|
235
|
+
name: "rect",
|
|
236
|
+
type: "element",
|
|
237
|
+
attributes: {
|
|
238
|
+
class: "pcb-boundary",
|
|
239
|
+
x: x.toString(),
|
|
240
|
+
y: y.toString(),
|
|
241
|
+
width: width.toString(),
|
|
242
|
+
height: height.toString()
|
|
243
|
+
}
|
|
244
|
+
};
|
|
142
245
|
}
|
|
143
246
|
|
|
144
247
|
// src/lib/soup-to-svg.ts
|
|
145
|
-
|
|
146
|
-
|
|
248
|
+
var import_svgson2 = require("svgson");
|
|
249
|
+
function circuitJsonToSchematicSvg(soup) {
|
|
147
250
|
let minX = Number.POSITIVE_INFINITY;
|
|
148
251
|
let minY = Number.POSITIVE_INFINITY;
|
|
149
252
|
let maxX = Number.NEGATIVE_INFINITY;
|
|
150
253
|
let maxY = Number.NEGATIVE_INFINITY;
|
|
254
|
+
const portSize = 0.2;
|
|
255
|
+
const portPositions = /* @__PURE__ */ new Map();
|
|
256
|
+
for (const item of soup) {
|
|
257
|
+
if (item.type === "schematic_component") {
|
|
258
|
+
updateBounds(item.center, item.size, item.rotation || 0);
|
|
259
|
+
} else if (item.type === "schematic_port") {
|
|
260
|
+
updateBounds(item.center, { width: portSize, height: portSize }, 0);
|
|
261
|
+
portPositions.set(item.schematic_port_id, item.center);
|
|
262
|
+
} else if (item.type === "schematic_text") {
|
|
263
|
+
updateBounds(item.position, { width: 0, height: 0 }, 0);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const height = maxY - minY;
|
|
267
|
+
const flipY = (y) => height - (y - minY) + minY;
|
|
268
|
+
const svgChildren = [];
|
|
269
|
+
const componentMap = /* @__PURE__ */ new Map();
|
|
151
270
|
for (const component of soup.filter(
|
|
152
271
|
(item) => item.type === "schematic_component"
|
|
153
272
|
)) {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
273
|
+
const flippedCenter = {
|
|
274
|
+
x: component.center.x,
|
|
275
|
+
y: flipY(component.center.y)
|
|
276
|
+
};
|
|
277
|
+
const svg = createSchematicComponent(
|
|
278
|
+
flippedCenter,
|
|
279
|
+
component.size,
|
|
280
|
+
component.rotation || 0
|
|
281
|
+
);
|
|
282
|
+
svgChildren.push(svg);
|
|
283
|
+
componentMap.set(component.schematic_component_id, component);
|
|
284
|
+
}
|
|
285
|
+
for (const port of soup.filter((item) => item.type === "schematic_port")) {
|
|
286
|
+
const flippedCenter = { x: port.center.x, y: flipY(port.center.y) };
|
|
287
|
+
const svg = createSchematicPort(flippedCenter);
|
|
288
|
+
svgChildren.push(svg);
|
|
289
|
+
const component = componentMap.get(port.schematic_component_id);
|
|
290
|
+
if (component) {
|
|
291
|
+
const line = createPortToComponentLine(
|
|
292
|
+
flippedCenter,
|
|
293
|
+
component,
|
|
294
|
+
port.facing_direction || "right"
|
|
295
|
+
);
|
|
296
|
+
svgChildren.push(line);
|
|
158
297
|
}
|
|
159
298
|
}
|
|
160
299
|
for (const trace of soup.filter((item) => item.type === "schematic_trace")) {
|
|
161
|
-
const svg = createSchematicTrace(trace);
|
|
162
|
-
|
|
163
|
-
updateTraceBounds(trace.edges);
|
|
300
|
+
const svg = createSchematicTrace(trace, flipY, portPositions);
|
|
301
|
+
if (svg) svgChildren.push(svg);
|
|
164
302
|
}
|
|
165
303
|
for (const text of soup.filter((item) => item.type === "schematic_text")) {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
updateTextBounds(text.position);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
for (const label of soup.filter(
|
|
173
|
-
(item) => item.type === "schematic_net_label"
|
|
174
|
-
)) {
|
|
175
|
-
const svg = createSchematicNetLabel(label);
|
|
176
|
-
svgContent.push(svg);
|
|
177
|
-
const width2 = label.text.length * 0.15 + 0.3;
|
|
178
|
-
const height2 = 0.3;
|
|
179
|
-
minX = Math.min(minX, label.center.x);
|
|
180
|
-
minY = Math.min(minY, label.center.y - height2 / 2);
|
|
181
|
-
maxX = Math.max(maxX, label.center.x + width2);
|
|
182
|
-
maxY = Math.max(maxY, label.center.y + height2 / 2);
|
|
304
|
+
const flippedPosition = { x: text.position.x, y: flipY(text.position.y) };
|
|
305
|
+
const svg = createSchematicText(text, flippedPosition);
|
|
306
|
+
svgChildren.push(svg);
|
|
183
307
|
}
|
|
184
308
|
const padding = 1;
|
|
185
309
|
const width = maxX - minX + 2 * padding;
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
310
|
+
const viewBox = `${minX - padding} ${minY - padding} ${width} ${height + 2 * padding}`;
|
|
311
|
+
const svgObject = {
|
|
312
|
+
name: "svg",
|
|
313
|
+
type: "element",
|
|
314
|
+
attributes: {
|
|
315
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
316
|
+
viewBox,
|
|
317
|
+
width: "1200",
|
|
318
|
+
height: "600"
|
|
319
|
+
},
|
|
320
|
+
children: [
|
|
321
|
+
{
|
|
322
|
+
name: "style",
|
|
323
|
+
type: "element",
|
|
324
|
+
children: [
|
|
325
|
+
{
|
|
326
|
+
type: "text",
|
|
327
|
+
value: `
|
|
328
|
+
.component { fill: none; stroke: red; stroke-width: 0.03; }
|
|
329
|
+
.component-pin { fill: none; stroke: red; stroke-width: 0.03; }
|
|
330
|
+
.trace { stroke: green; stroke-width: 0.03; fill: none; }
|
|
331
|
+
.text { font-family: Arial, sans-serif; font-size: 0.2px; }
|
|
332
|
+
.port { fill: none; stroke: blue; stroke-width: 0.03; }
|
|
333
|
+
`
|
|
334
|
+
}
|
|
335
|
+
]
|
|
336
|
+
},
|
|
337
|
+
...svgChildren
|
|
338
|
+
]
|
|
339
|
+
};
|
|
340
|
+
return (0, import_svgson2.stringify)({ value: "", ...svgObject });
|
|
200
341
|
function updateBounds(center, size, rotation) {
|
|
201
342
|
const corners = [
|
|
202
343
|
{ x: -size.width / 2, y: -size.height / 2 },
|
|
@@ -213,121 +354,133 @@ function soupToSvg(soup) {
|
|
|
213
354
|
maxY = Math.max(maxY, rotatedY);
|
|
214
355
|
}
|
|
215
356
|
}
|
|
216
|
-
function updateTraceBounds(edges) {
|
|
217
|
-
for (const edge of edges) {
|
|
218
|
-
minX = Math.min(minX, edge.from.x, edge.to.x);
|
|
219
|
-
minY = Math.min(minY, edge.from.y, edge.to.y);
|
|
220
|
-
maxX = Math.max(maxX, edge.from.x, edge.to.x);
|
|
221
|
-
maxY = Math.max(maxY, edge.from.y, edge.to.y);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
function updateTextBounds(position) {
|
|
225
|
-
minX = Math.min(minX, position.x);
|
|
226
|
-
minY = Math.min(minY, position.y);
|
|
227
|
-
maxX = Math.max(maxX, position.x);
|
|
228
|
-
maxY = Math.max(maxY, position.y);
|
|
229
|
-
}
|
|
230
357
|
}
|
|
231
|
-
function createSchematicComponent(
|
|
232
|
-
const { center, size, rotation } = component;
|
|
358
|
+
function createSchematicComponent(center, size, rotation) {
|
|
233
359
|
const transform = `translate(${center.x}, ${center.y}) rotate(${rotation * 180 / Math.PI})`;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
class="component-pin"
|
|
253
|
-
x="${size.width / 2 - pinSize / 2}"
|
|
254
|
-
y="${-pinSize / 2}"
|
|
255
|
-
width="${pinSize}"
|
|
256
|
-
height="${pinSize}"
|
|
257
|
-
/>
|
|
258
|
-
</g>
|
|
259
|
-
`;
|
|
360
|
+
return {
|
|
361
|
+
name: "g",
|
|
362
|
+
type: "element",
|
|
363
|
+
attributes: { transform },
|
|
364
|
+
children: [
|
|
365
|
+
{
|
|
366
|
+
name: "rect",
|
|
367
|
+
type: "element",
|
|
368
|
+
attributes: {
|
|
369
|
+
class: "component",
|
|
370
|
+
x: (-size.width / 2).toString(),
|
|
371
|
+
y: (-size.height / 2).toString(),
|
|
372
|
+
width: size.width.toString(),
|
|
373
|
+
height: size.height.toString()
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
]
|
|
377
|
+
};
|
|
260
378
|
}
|
|
261
|
-
function
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
379
|
+
function createSchematicPort(center) {
|
|
380
|
+
const portSize = 0.2;
|
|
381
|
+
const x = center.x - portSize / 2;
|
|
382
|
+
const y = center.y - portSize / 2;
|
|
383
|
+
return {
|
|
384
|
+
name: "rect",
|
|
385
|
+
type: "element",
|
|
386
|
+
attributes: {
|
|
387
|
+
class: "port",
|
|
388
|
+
x: x.toString(),
|
|
389
|
+
y: y.toString(),
|
|
390
|
+
width: portSize.toString(),
|
|
391
|
+
height: portSize.toString()
|
|
267
392
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
function createPortToComponentLine(portCenter, component, facingDirection) {
|
|
396
|
+
const componentCenter = { x: component.center.x, y: portCenter.y };
|
|
397
|
+
const halfWidth = component.size.width / 2;
|
|
398
|
+
const halfHeight = component.size.height / 2;
|
|
399
|
+
let endX = portCenter.x;
|
|
400
|
+
let endY = portCenter.y;
|
|
401
|
+
switch (facingDirection) {
|
|
402
|
+
case "left":
|
|
403
|
+
endX = componentCenter.x - halfWidth;
|
|
404
|
+
break;
|
|
405
|
+
case "right":
|
|
406
|
+
endX = componentCenter.x + halfWidth;
|
|
407
|
+
break;
|
|
408
|
+
case "up":
|
|
409
|
+
endY = componentCenter.y - halfHeight;
|
|
410
|
+
break;
|
|
411
|
+
case "down":
|
|
412
|
+
endY = componentCenter.y + halfHeight;
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
name: "line",
|
|
417
|
+
type: "element",
|
|
418
|
+
attributes: {
|
|
419
|
+
class: "component-pin",
|
|
420
|
+
x1: portCenter.x.toString(),
|
|
421
|
+
y1: portCenter.y.toString(),
|
|
422
|
+
x2: endX.toString(),
|
|
423
|
+
y2: endY.toString()
|
|
271
424
|
}
|
|
272
|
-
|
|
273
|
-
}).join(" ");
|
|
274
|
-
return `<path class="trace" d="${path}" />`;
|
|
425
|
+
};
|
|
275
426
|
}
|
|
276
|
-
function
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
427
|
+
function createSchematicTrace(trace, flipY, portPositions) {
|
|
428
|
+
const edges = trace.edges;
|
|
429
|
+
if (edges.length === 0) return null;
|
|
430
|
+
let path = "";
|
|
431
|
+
edges.forEach((edge, index) => {
|
|
432
|
+
const fromPoint = edge.from.ti !== void 0 ? portPositions.get(edge.from.ti) : edge.from;
|
|
433
|
+
const toPoint = edge.to.ti !== void 0 ? portPositions.get(edge.to.ti) : edge.to;
|
|
434
|
+
if (!fromPoint || !toPoint) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const fromCoord = `${fromPoint.x} ${flipY(fromPoint.y)}`;
|
|
438
|
+
const toCoord = `${toPoint.x} ${flipY(toPoint.y)}`;
|
|
439
|
+
if (index === 0) {
|
|
440
|
+
path += `M ${fromCoord} L ${toCoord}`;
|
|
441
|
+
} else {
|
|
442
|
+
path += ` L ${toCoord}`;
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
if (trace.to_schematic_port_id) {
|
|
446
|
+
const finalPort = portPositions.get(trace.to_schematic_port_id);
|
|
447
|
+
if (finalPort) {
|
|
448
|
+
const lastFromPoint = path.split("M")[1]?.split("L")[0];
|
|
449
|
+
const lastEdge = edges[edges.length - 1];
|
|
450
|
+
const lastPoint = lastEdge.to.ti !== void 0 ? portPositions.get(lastEdge.to.ti) : lastEdge.to;
|
|
451
|
+
if (lastPoint.x !== finalPort.x || lastPoint.y !== finalPort.y) {
|
|
452
|
+
const finalCoord = `${finalPort.x} ${flipY(finalPort.y)}`;
|
|
453
|
+
path += ` M ${lastFromPoint} L ${finalCoord}`;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return path ? {
|
|
458
|
+
name: "path",
|
|
459
|
+
type: "element",
|
|
460
|
+
attributes: {
|
|
461
|
+
class: "trace",
|
|
462
|
+
d: path
|
|
463
|
+
}
|
|
464
|
+
} : null;
|
|
285
465
|
}
|
|
286
|
-
function
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
L ${labelCenterX},${label.center.y + height / 2}
|
|
305
|
-
Z
|
|
306
|
-
`;
|
|
307
|
-
const textX = labelCenterX + width / 2;
|
|
308
|
-
const connectingLine = `
|
|
309
|
-
<line
|
|
310
|
-
x1="${labelCenterX + width}"
|
|
311
|
-
y1="${label.center.y}"
|
|
312
|
-
x2="${label.center.x}"
|
|
313
|
-
y2="${label.center.y}"
|
|
314
|
-
stroke="green"
|
|
315
|
-
stroke-width="0.05"
|
|
316
|
-
/>
|
|
317
|
-
`;
|
|
318
|
-
return `
|
|
319
|
-
<g class="net-label">
|
|
320
|
-
${connectingLine}
|
|
321
|
-
<path d="${path}" fill="white" stroke="black" stroke-width="0.02"/>
|
|
322
|
-
<text
|
|
323
|
-
x="${textX}"
|
|
324
|
-
y="${label.center.y}"
|
|
325
|
-
text-anchor="middle"
|
|
326
|
-
dominant-baseline="central"
|
|
327
|
-
fill="black"
|
|
328
|
-
>${label.text}</text>
|
|
329
|
-
</g>
|
|
330
|
-
`;
|
|
466
|
+
function createSchematicText(text, position) {
|
|
467
|
+
return {
|
|
468
|
+
name: "text",
|
|
469
|
+
type: "element",
|
|
470
|
+
attributes: {
|
|
471
|
+
class: "text",
|
|
472
|
+
x: position.x.toString(),
|
|
473
|
+
y: position.y.toString(),
|
|
474
|
+
"text-anchor": getTextAnchor(text.anchor),
|
|
475
|
+
"dominant-baseline": "middle"
|
|
476
|
+
},
|
|
477
|
+
children: [
|
|
478
|
+
{
|
|
479
|
+
type: "text",
|
|
480
|
+
value: text.text ? text.text : ""
|
|
481
|
+
}
|
|
482
|
+
]
|
|
483
|
+
};
|
|
331
484
|
}
|
|
332
485
|
function getTextAnchor(anchor) {
|
|
333
486
|
switch (anchor) {
|
|
@@ -341,7 +494,7 @@ function getTextAnchor(anchor) {
|
|
|
341
494
|
}
|
|
342
495
|
// Annotate the CommonJS export names for ESM import in node:
|
|
343
496
|
0 && (module.exports = {
|
|
344
|
-
|
|
345
|
-
|
|
497
|
+
circuitJsonToPcbSvg,
|
|
498
|
+
circuitJsonToSchematicSvg
|
|
346
499
|
});
|
|
347
500
|
//# sourceMappingURL=index.js.map
|
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\";\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"]}
|
|
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 { stringify, type INode } from \"svgson\";\nimport { applyToPoint, compose, scale, translate } from \"transformation-matrix\";\n\ninterface SvgObject {\n name: string;\n type: 'element' | 'text';\n attributes?: { [key: string]: string };\n children?: SvgObject[];\n value?: string;\n}\n\nfunction circuitJsonToPcbSvg(soup: AnySoupElement[]): 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 const svgElements = soup.map(item => {\n const element = createSvgElement(item, transform);\n if (element === null) {\n console.warn(\"Null element created for item:\", item);\n }\n return element;\n }).filter(element => element !== null);\n\n const svgObject: SvgObject = {\n name: 'svg',\n type: 'element',\n attributes: {\n xmlns: 'http://www.w3.org/2000/svg',\n width: svgWidth.toString(),\n height: svgHeight.toString(),\n },\n children: [\n {\n name: 'style',\n type: 'element',\n children: [\n {\n type: 'text',\n value: `\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 `\n }\n ]\n },\n {\n name: 'rect',\n type: 'element',\n attributes: {\n class: 'pcb-board',\n x: '0',\n y: '0',\n width: svgWidth.toString(),\n height: svgHeight.toString(),\n }\n },\n ...svgElements,\n createPcbBoundary(transform, minX, minY, maxX, maxY)\n ].filter(child => child !== null)\n };\n\n console.log('SVG Object:', JSON.stringify(svgObject, null, 2));\n\n try {\n return stringify(svgObject as INode);\n } catch (error) {\n console.error('Error stringifying SVG object:', error);\n console.log('Problematic SVG object:', JSON.stringify(svgObject, null, 2));\n throw error;\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 createSvgElement(item: AnySoupElement, transform: any): any {\n switch (item.type) {\n case 'pcb_component':\n return createPcbComponent(item, transform);\n case 'pcb_trace':\n return createPcbTrace(item, transform);\n case 'pcb_plated_hole':\n return createPcbHole(item, transform);\n case 'pcb_smtpad':\n return createPcbSMTPad(item, transform);\n default:\n return null;\n }\n}\n\nfunction createPcbComponent(component: any, transform: any): any {\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)`;\n\n return {\n name: 'g',\n type: 'element',\n attributes: { transform: transformStr },\n children: [\n {\n name: 'rect',\n type: 'element',\n attributes: {\n class: 'pcb-component',\n x: (-scaledWidth / 2).toString(),\n y: (-scaledHeight / 2).toString(),\n width: scaledWidth.toString(),\n height: scaledHeight.toString(),\n }\n },\n {\n name: 'rect',\n type: 'element',\n attributes: {\n class: 'pcb-component-outline',\n x: (-scaledWidth / 2).toString(),\n y: (-scaledHeight / 2).toString(),\n width: scaledWidth.toString(),\n height: scaledHeight.toString(),\n }\n }\n ]\n };\n}\n\nfunction createPcbHole(hole: any, transform: any): any {\n const [x, y] = applyToPoint(transform, [hole.x, hole.y]);\n const scaledRadius = (hole.outer_diameter / 2) * Math.abs(transform.a);\n return {\n name: 'circle',\n type: 'element',\n attributes: {\n class: 'pcb-hole',\n cx: x.toString(),\n cy: y.toString(),\n r: scaledRadius.toString(),\n }\n };\n}\n\nfunction createPcbSMTPad(pad: any, transform: any): any {\n const [x, y] = applyToPoint(transform, [pad.x, pad.y]);\n const width = pad.width * Math.abs(transform.a);\n const height = pad.height * Math.abs(transform.d);\n return {\n name: 'rect',\n type: 'element',\n attributes: {\n class: 'pcb-pad',\n x: (x - width / 2).toString(),\n y: (y - height / 2).toString(),\n width: width.toString(),\n height: height.toString(),\n }\n };\n}\n\nfunction createPcbTrace(trace: any, transform: any): any {\n if (!trace.route || !Array.isArray(trace.route)) return null;\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 {\n name: 'path',\n type: 'element',\n attributes: {\n class: 'pcb-trace',\n d: path,\n }\n };\n}\n\nfunction createPcbBoundary(transform: any, minX: number, minY: number, maxX: number, maxY: number): any {\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 {\n name: 'rect',\n type: 'element',\n attributes: {\n class: 'pcb-boundary',\n x: x.toString(),\n y: y.toString(),\n width: width.toString(),\n height: height.toString(),\n }\n };\n}\n\nexport { circuitJsonToPcbSvg };\n","import type { AnySoupElement } from \"@tscircuit/soup\";\nimport { stringify } from \"svgson\";\n\nfunction circuitJsonToSchematicSvg(soup: AnySoupElement[]): 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 const portSize = 0.2;\n const portPositions = new Map();\n\n // First pass: find the bounds and collect port positions\n for (const item of soup) {\n if (item.type === \"schematic_component\") {\n updateBounds(item.center, item.size, item.rotation || 0);\n } else if (item.type === \"schematic_port\") {\n updateBounds(item.center, { width: portSize, height: portSize }, 0);\n portPositions.set(item.schematic_port_id, item.center);\n } else if (item.type === \"schematic_text\") {\n updateBounds(item.position, { width: 0, height: 0 }, 0);\n }\n }\n\n const height = maxY - minY;\n const flipY = (y: number) => height - (y - minY) + minY;\n\n const svgChildren: any[] = [];\n\n // Process components\n const componentMap = new Map();\n for (const component of soup.filter(\n (item) => item.type === \"schematic_component\"\n )) {\n const flippedCenter = {\n x: component.center.x,\n y: flipY(component.center.y),\n };\n const svg = createSchematicComponent(\n flippedCenter,\n component.size,\n component.rotation || 0\n );\n svgChildren.push(svg);\n componentMap.set(component.schematic_component_id, component);\n }\n\n // Process ports and add lines to component edges\n for (const port of soup.filter((item) => item.type === \"schematic_port\")) {\n const flippedCenter = { x: port.center.x, y: flipY(port.center.y) };\n const svg = createSchematicPort(flippedCenter);\n svgChildren.push(svg);\n\n const component = componentMap.get(port.schematic_component_id);\n if (component) {\n const line = createPortToComponentLine(\n flippedCenter,\n component,\n port.facing_direction || \"right\"\n );\n svgChildren.push(line);\n }\n }\n\n // Process schematic traces\n for (const trace of soup.filter((item) => item.type === \"schematic_trace\")) {\n const svg = createSchematicTrace(trace, flipY, portPositions);\n if (svg) svgChildren.push(svg);\n }\n\n // Process text\n for (const text of soup.filter((item) => item.type === \"schematic_text\")) {\n const flippedPosition = { x: text.position.x, y: flipY(text.position.y) };\n const svg = createSchematicText(text, flippedPosition);\n svgChildren.push(svg);\n }\n\n const padding = 1;\n const width = maxX - minX + 2 * padding;\n const viewBox = `${minX - padding} ${minY - padding} ${width} ${height + 2 * padding}`;\n\n const svgObject = {\n name: 'svg',\n type: 'element',\n attributes: {\n xmlns: 'http://www.w3.org/2000/svg',\n viewBox,\n width: '1200',\n height: '600',\n },\n children: [\n {\n name: 'style',\n type: 'element',\n children: [\n {\n type: 'text',\n value: `\n .component { fill: none; stroke: red; stroke-width: 0.03; }\n .component-pin { fill: none; stroke: red; stroke-width: 0.03; }\n .trace { stroke: green; stroke-width: 0.03; fill: none; }\n .text { font-family: Arial, sans-serif; font-size: 0.2px; }\n .port { fill: none; stroke: blue; stroke-width: 0.03; }\n `\n }\n ]\n },\n ...svgChildren\n ]\n };\n\n return stringify({ value: '', ...svgObject});\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\nfunction createSchematicComponent(\n center: { x: number; y: number },\n size: { width: number; height: number },\n rotation: number\n): any {\n const transform = `translate(${center.x}, ${center.y}) rotate(${(rotation * 180) / Math.PI})`;\n\n return {\n name: 'g',\n type: 'element',\n attributes: { transform },\n children: [\n {\n name: 'rect',\n type: 'element',\n attributes: {\n class: 'component',\n x: (-size.width / 2).toString(),\n y: (-size.height / 2).toString(),\n width: size.width.toString(),\n height: size.height.toString(),\n }\n }\n ]\n };\n}\n\nfunction createSchematicPort(center: { x: number; y: number }): any {\n const portSize = 0.2;\n const x = center.x - portSize / 2;\n const y = center.y - portSize / 2;\n\n return {\n name: 'rect',\n type: 'element',\n attributes: {\n class: 'port',\n x: x.toString(),\n y: y.toString(),\n width: portSize.toString(),\n height: portSize.toString(),\n }\n };\n}\n\nfunction createPortToComponentLine(\n portCenter: { x: number; y: number },\n component: any,\n facingDirection: string\n): any {\n const componentCenter = { x: component.center.x, y: portCenter.y };\n const halfWidth = component.size.width / 2;\n const halfHeight = component.size.height / 2;\n\n let endX = portCenter.x;\n let endY = portCenter.y;\n\n switch (facingDirection) {\n case \"left\":\n endX = componentCenter.x - halfWidth;\n break;\n case \"right\":\n endX = componentCenter.x + halfWidth;\n break;\n case \"up\":\n endY = componentCenter.y - halfHeight;\n break;\n case \"down\":\n endY = componentCenter.y + halfHeight;\n break;\n }\n\n return {\n name: 'line',\n type: 'element',\n attributes: {\n class: 'component-pin',\n x1: portCenter.x.toString(),\n y1: portCenter.y.toString(),\n x2: endX.toString(),\n y2: endY.toString(),\n }\n };\n}\n\nfunction createSchematicTrace(\n trace: any,\n flipY: (y: number) => number,\n portPositions: Map<string, { x: number; y: number }>\n): any {\n const edges = trace.edges;\n if (edges.length === 0) return null;\n\n let path = \"\";\n\n // Process all edges\n edges.forEach((edge: any, index: number) => {\n const fromPoint =\n edge.from.ti !== undefined ? portPositions.get(edge.from.ti) : edge.from;\n const toPoint =\n edge.to.ti !== undefined ? portPositions.get(edge.to.ti) : edge.to;\n\n if (!fromPoint || !toPoint) {\n return;\n }\n\n const fromCoord = `${fromPoint.x} ${flipY(fromPoint.y)}`;\n const toCoord = `${toPoint.x} ${flipY(toPoint.y)}`;\n\n if (index === 0) {\n path += `M ${fromCoord} L ${toCoord}`;\n } else {\n path += ` L ${toCoord}`;\n }\n });\n\n // Handle connection to final port if needed\n if (trace.to_schematic_port_id) {\n const finalPort = portPositions.get(trace.to_schematic_port_id);\n if (finalPort) {\n const lastFromPoint = path.split(\"M\")[1]?.split(\"L\")[0];\n const lastEdge = edges[edges.length - 1];\n const lastPoint =\n lastEdge.to.ti !== undefined\n ? portPositions.get(lastEdge.to.ti)\n : lastEdge.to;\n if (lastPoint.x !== finalPort.x || lastPoint.y !== finalPort.y) {\n const finalCoord = `${finalPort.x} ${flipY(finalPort.y)}`;\n path += ` M ${lastFromPoint} L ${finalCoord}`;\n }\n }\n }\n\n return path ? {\n name: 'path',\n type: 'element',\n attributes: {\n class: 'trace',\n d: path,\n }\n } : null;\n}\n\nfunction createSchematicText(\n text: any,\n position: { x: number; y: number }\n): any {\n return {\n name: 'text',\n type: 'element',\n attributes: {\n class: 'text',\n x: position.x.toString(),\n y: position.y.toString(),\n 'text-anchor': getTextAnchor(text.anchor),\n 'dominant-baseline': 'middle',\n },\n children: [\n {\n type: 'text',\n value: text.text ? text.text : \"\",\n }\n ]\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 { circuitJsonToSchematicSvg };"],"mappings":"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","names":["import_svgson"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circuit-to-svg",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Convert Circuit JSON to SVG",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@tscircuit/routing": "^1.3.5",
|
|
39
|
+
"svgson": "^5.3.1",
|
|
39
40
|
"transformation-matrix": "^2.16.1"
|
|
40
41
|
}
|
|
41
42
|
}
|