circuit-to-svg 0.0.208 → 0.0.209
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 +21 -9
- package/dist/index.js +360 -243
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -285,14 +285,6 @@ declare function convertCircuitJsonToSchematicSvg(circuitJson: AnyCircuitElement
|
|
|
285
285
|
*/
|
|
286
286
|
declare const circuitJsonToSchematicSvg: typeof convertCircuitJsonToSchematicSvg;
|
|
287
287
|
|
|
288
|
-
interface Options {
|
|
289
|
-
layer: "top" | "bottom";
|
|
290
|
-
width?: number;
|
|
291
|
-
height?: number;
|
|
292
|
-
includeVersion?: boolean;
|
|
293
|
-
}
|
|
294
|
-
declare function convertCircuitJsonToSolderPasteMask(circuitJson: AnyCircuitElement[], options: Options): string;
|
|
295
|
-
|
|
296
288
|
type ExperimentType = string;
|
|
297
289
|
interface SimulationExperimentElement {
|
|
298
290
|
type: "simulation_experiment";
|
|
@@ -315,6 +307,26 @@ interface SimulationTransientVoltageGraphElement {
|
|
|
315
307
|
}
|
|
316
308
|
type CircuitJsonWithSimulation = AnyCircuitElement | SimulationExperimentElement | SimulationTransientVoltageGraphElement;
|
|
317
309
|
|
|
310
|
+
interface ConvertSchematicSimulationParams {
|
|
311
|
+
circuitJson: CircuitJsonWithSimulation[];
|
|
312
|
+
simulation_experiment_id: string;
|
|
313
|
+
simulation_transient_voltage_graph_ids?: string[];
|
|
314
|
+
width?: number;
|
|
315
|
+
height?: number;
|
|
316
|
+
schematicHeightRatio?: number;
|
|
317
|
+
schematicOptions?: Omit<Parameters<typeof convertCircuitJsonToSchematicSvg>[1], "width" | "height" | "includeVersion">;
|
|
318
|
+
includeVersion?: boolean;
|
|
319
|
+
}
|
|
320
|
+
declare function convertCircuitJsonToSchematicSimulationSvg({ circuitJson, simulation_experiment_id, simulation_transient_voltage_graph_ids, width, height, schematicHeightRatio, schematicOptions, includeVersion, }: ConvertSchematicSimulationParams): string;
|
|
321
|
+
|
|
322
|
+
interface Options {
|
|
323
|
+
layer: "top" | "bottom";
|
|
324
|
+
width?: number;
|
|
325
|
+
height?: number;
|
|
326
|
+
includeVersion?: boolean;
|
|
327
|
+
}
|
|
328
|
+
declare function convertCircuitJsonToSolderPasteMask(circuitJson: AnyCircuitElement[], options: Options): string;
|
|
329
|
+
|
|
318
330
|
interface ConvertSimulationGraphParams {
|
|
319
331
|
circuitJson: CircuitJsonWithSimulation[];
|
|
320
332
|
simulation_experiment_id: string;
|
|
@@ -335,4 +347,4 @@ declare const createSvgObjectsForSchComponentPortHovers: ({ component, transform
|
|
|
335
347
|
circuitJson: AnyCircuitElement[];
|
|
336
348
|
}) => INode[];
|
|
337
349
|
|
|
338
|
-
export { type AssemblySvgContext, CIRCUIT_TO_SVG_VERSION, type ColorMap, type ColorOverrides, type PcbColorMap, type PcbColorOverrides, type PcbContext, type PinoutSvgContext, circuitJsonToPcbSvg, circuitJsonToSchematicSvg, convertCircuitJsonToAssemblySvg, convertCircuitJsonToPcbSvg, convertCircuitJsonToPinoutSvg, convertCircuitJsonToSchematicSvg, convertCircuitJsonToSimulationGraphSvg, convertCircuitJsonToSolderPasteMask, createSvgObjectsForSchComponentPortHovers, getSoftwareUsedString };
|
|
350
|
+
export { type AssemblySvgContext, CIRCUIT_TO_SVG_VERSION, type ColorMap, type ColorOverrides, type PcbColorMap, type PcbColorOverrides, type PcbContext, type PinoutSvgContext, circuitJsonToPcbSvg, circuitJsonToSchematicSvg, convertCircuitJsonToAssemblySvg, convertCircuitJsonToPcbSvg, convertCircuitJsonToPinoutSvg, convertCircuitJsonToSchematicSimulationSvg, convertCircuitJsonToSchematicSvg, convertCircuitJsonToSimulationGraphSvg, convertCircuitJsonToSolderPasteMask, createSvgObjectsForSchComponentPortHovers, getSoftwareUsedString };
|
package/dist/index.js
CHANGED
|
@@ -1783,7 +1783,7 @@ function getSoftwareUsedString(circuitJson) {
|
|
|
1783
1783
|
var package_default = {
|
|
1784
1784
|
name: "circuit-to-svg",
|
|
1785
1785
|
type: "module",
|
|
1786
|
-
version: "0.0.
|
|
1786
|
+
version: "0.0.208",
|
|
1787
1787
|
description: "Convert Circuit JSON to SVG",
|
|
1788
1788
|
main: "dist/index.js",
|
|
1789
1789
|
files: [
|
|
@@ -7933,249 +7933,11 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
|
|
|
7933
7933
|
}
|
|
7934
7934
|
var circuitJsonToSchematicSvg = convertCircuitJsonToSchematicSvg;
|
|
7935
7935
|
|
|
7936
|
-
// lib/
|
|
7937
|
-
import { stringify as
|
|
7938
|
-
import {
|
|
7939
|
-
applyToPoint as applyToPoint60,
|
|
7940
|
-
compose as compose14,
|
|
7941
|
-
scale as scale9,
|
|
7942
|
-
translate as translate14
|
|
7943
|
-
} from "transformation-matrix";
|
|
7944
|
-
|
|
7945
|
-
// lib/pcb/svg-object-fns/convert-circuit-json-to-solder-paste-mask.ts
|
|
7946
|
-
import { applyToPoint as applyToPoint59 } from "transformation-matrix";
|
|
7947
|
-
function createSvgObjectsFromSolderPaste(solderPaste, ctx) {
|
|
7948
|
-
const { transform, layer: layerFilter } = ctx;
|
|
7949
|
-
if (layerFilter && solderPaste.layer !== layerFilter) return [];
|
|
7950
|
-
const [x, y] = applyToPoint59(transform, [solderPaste.x, solderPaste.y]);
|
|
7951
|
-
if (solderPaste.shape === "rect" || solderPaste.shape === "rotated_rect") {
|
|
7952
|
-
const width = solderPaste.width * Math.abs(transform.a);
|
|
7953
|
-
const height = solderPaste.height * Math.abs(transform.d);
|
|
7954
|
-
if (solderPaste.shape === "rotated_rect" && solderPaste.ccw_rotation) {
|
|
7955
|
-
return [
|
|
7956
|
-
{
|
|
7957
|
-
name: "rect",
|
|
7958
|
-
type: "element",
|
|
7959
|
-
attributes: {
|
|
7960
|
-
class: "pcb-solder-paste",
|
|
7961
|
-
fill: solderPasteLayerNameToColor(solderPaste.layer),
|
|
7962
|
-
x: (-width / 2).toString(),
|
|
7963
|
-
y: (-height / 2).toString(),
|
|
7964
|
-
width: width.toString(),
|
|
7965
|
-
height: height.toString(),
|
|
7966
|
-
transform: `translate(${x} ${y}) rotate(${-solderPaste.ccw_rotation})`
|
|
7967
|
-
}
|
|
7968
|
-
}
|
|
7969
|
-
];
|
|
7970
|
-
}
|
|
7971
|
-
return [
|
|
7972
|
-
{
|
|
7973
|
-
name: "rect",
|
|
7974
|
-
type: "element",
|
|
7975
|
-
attributes: {
|
|
7976
|
-
class: "pcb-solder-paste",
|
|
7977
|
-
fill: solderPasteLayerNameToColor(solderPaste.layer),
|
|
7978
|
-
x: (x - width / 2).toString(),
|
|
7979
|
-
y: (y - height / 2).toString(),
|
|
7980
|
-
width: width.toString(),
|
|
7981
|
-
height: height.toString()
|
|
7982
|
-
}
|
|
7983
|
-
}
|
|
7984
|
-
];
|
|
7985
|
-
}
|
|
7986
|
-
if (solderPaste.shape === "pill") {
|
|
7987
|
-
const width = solderPaste.width * Math.abs(transform.a);
|
|
7988
|
-
const height = solderPaste.height * Math.abs(transform.d);
|
|
7989
|
-
const radius = solderPaste.radius * Math.abs(transform.a);
|
|
7990
|
-
return [
|
|
7991
|
-
{
|
|
7992
|
-
name: "rect",
|
|
7993
|
-
type: "element",
|
|
7994
|
-
attributes: {
|
|
7995
|
-
class: "pcb-solder-paste",
|
|
7996
|
-
fill: solderPasteLayerNameToColor(solderPaste.layer),
|
|
7997
|
-
x: (x - width / 2).toString(),
|
|
7998
|
-
y: (y - height / 2).toString(),
|
|
7999
|
-
width: width.toString(),
|
|
8000
|
-
height: height.toString(),
|
|
8001
|
-
rx: radius.toString()
|
|
8002
|
-
}
|
|
8003
|
-
}
|
|
8004
|
-
];
|
|
8005
|
-
}
|
|
8006
|
-
if (solderPaste.shape === "circle") {
|
|
8007
|
-
const radius = solderPaste.radius * Math.abs(transform.a);
|
|
8008
|
-
return [
|
|
8009
|
-
{
|
|
8010
|
-
name: "circle",
|
|
8011
|
-
type: "element",
|
|
8012
|
-
attributes: {
|
|
8013
|
-
class: "pcb-solder-paste",
|
|
8014
|
-
fill: solderPasteLayerNameToColor(solderPaste.layer),
|
|
8015
|
-
cx: x.toString(),
|
|
8016
|
-
cy: y.toString(),
|
|
8017
|
-
r: radius.toString()
|
|
8018
|
-
}
|
|
8019
|
-
}
|
|
8020
|
-
];
|
|
8021
|
-
}
|
|
8022
|
-
}
|
|
8023
|
-
|
|
8024
|
-
// lib/pcb/convert-circuit-json-to-solder-paste-mask.ts
|
|
8025
|
-
var OBJECT_ORDER4 = [
|
|
8026
|
-
"pcb_board",
|
|
8027
|
-
"pcb_solder_paste"
|
|
8028
|
-
];
|
|
8029
|
-
function convertCircuitJsonToSolderPasteMask(circuitJson, options) {
|
|
8030
|
-
let minX = Number.POSITIVE_INFINITY;
|
|
8031
|
-
let minY = Number.POSITIVE_INFINITY;
|
|
8032
|
-
let maxX = Number.NEGATIVE_INFINITY;
|
|
8033
|
-
let maxY = Number.NEGATIVE_INFINITY;
|
|
8034
|
-
const filteredCircuitJson = circuitJson.filter(
|
|
8035
|
-
(elm) => elm.type === "pcb_board" || elm.type === "pcb_solder_paste" && elm.layer === options.layer
|
|
8036
|
-
);
|
|
8037
|
-
for (const item of filteredCircuitJson) {
|
|
8038
|
-
if (item.type === "pcb_board") {
|
|
8039
|
-
if (item.outline && Array.isArray(item.outline) && item.outline.length >= 3) {
|
|
8040
|
-
updateBoundsToIncludeOutline(item.outline);
|
|
8041
|
-
} else if ("center" in item && "width" in item && "height" in item) {
|
|
8042
|
-
updateBounds(item.center, item.width, item.height);
|
|
8043
|
-
}
|
|
8044
|
-
} else if (item.type === "pcb_solder_paste" && "x" in item && "y" in item) {
|
|
8045
|
-
updateBounds({ x: item.x, y: item.y }, 0, 0);
|
|
8046
|
-
}
|
|
8047
|
-
}
|
|
8048
|
-
const padding = 1;
|
|
8049
|
-
const circuitWidth = maxX - minX + 2 * padding;
|
|
8050
|
-
const circuitHeight = maxY - minY + 2 * padding;
|
|
8051
|
-
const svgWidth = options.width ?? 800;
|
|
8052
|
-
const svgHeight = options.height ?? 600;
|
|
8053
|
-
const scaleX = svgWidth / circuitWidth;
|
|
8054
|
-
const scaleY = svgHeight / circuitHeight;
|
|
8055
|
-
const scaleFactor = Math.min(scaleX, scaleY);
|
|
8056
|
-
const offsetX = (svgWidth - circuitWidth * scaleFactor) / 2;
|
|
8057
|
-
const offsetY = (svgHeight - circuitHeight * scaleFactor) / 2;
|
|
8058
|
-
const transform = compose14(
|
|
8059
|
-
translate14(
|
|
8060
|
-
offsetX - minX * scaleFactor + padding * scaleFactor,
|
|
8061
|
-
svgHeight - offsetY + minY * scaleFactor - padding * scaleFactor
|
|
8062
|
-
),
|
|
8063
|
-
scale9(scaleFactor, -scaleFactor)
|
|
8064
|
-
// Flip in y-direction
|
|
8065
|
-
);
|
|
8066
|
-
const ctx = {
|
|
8067
|
-
transform,
|
|
8068
|
-
layer: options.layer,
|
|
8069
|
-
colorMap: DEFAULT_PCB_COLOR_MAP
|
|
8070
|
-
};
|
|
8071
|
-
const svgObjects = filteredCircuitJson.sort(
|
|
8072
|
-
(a, b) => (OBJECT_ORDER4.indexOf(b.type) ?? 9999) - (OBJECT_ORDER4.indexOf(a.type) ?? 9999)
|
|
8073
|
-
).flatMap((item) => createSvgObjects4({ elm: item, ctx }));
|
|
8074
|
-
const softwareUsedString = getSoftwareUsedString(circuitJson);
|
|
8075
|
-
const version = CIRCUIT_TO_SVG_VERSION;
|
|
8076
|
-
const svgObject = {
|
|
8077
|
-
name: "svg",
|
|
8078
|
-
type: "element",
|
|
8079
|
-
attributes: {
|
|
8080
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
8081
|
-
width: svgWidth.toString(),
|
|
8082
|
-
height: svgHeight.toString(),
|
|
8083
|
-
...softwareUsedString && {
|
|
8084
|
-
"data-software-used-string": softwareUsedString
|
|
8085
|
-
},
|
|
8086
|
-
...options.includeVersion && {
|
|
8087
|
-
"data-circuit-to-svg-version": version
|
|
8088
|
-
}
|
|
8089
|
-
},
|
|
8090
|
-
value: "",
|
|
8091
|
-
children: [
|
|
8092
|
-
{
|
|
8093
|
-
name: "style",
|
|
8094
|
-
type: "element",
|
|
8095
|
-
children: [
|
|
8096
|
-
{
|
|
8097
|
-
type: "text",
|
|
8098
|
-
value: ""
|
|
8099
|
-
}
|
|
8100
|
-
]
|
|
8101
|
-
},
|
|
8102
|
-
{
|
|
8103
|
-
name: "rect",
|
|
8104
|
-
type: "element",
|
|
8105
|
-
attributes: {
|
|
8106
|
-
class: "boundary",
|
|
8107
|
-
x: "0",
|
|
8108
|
-
y: "0",
|
|
8109
|
-
fill: "#000",
|
|
8110
|
-
width: svgWidth.toString(),
|
|
8111
|
-
height: svgHeight.toString()
|
|
8112
|
-
}
|
|
8113
|
-
},
|
|
8114
|
-
createSvgObjectFromPcbBoundary2(transform, minX, minY, maxX, maxY),
|
|
8115
|
-
...svgObjects
|
|
8116
|
-
].filter((child) => child !== null)
|
|
8117
|
-
};
|
|
8118
|
-
try {
|
|
8119
|
-
return stringify5(svgObject);
|
|
8120
|
-
} catch (error) {
|
|
8121
|
-
console.error("Error stringifying SVG object:", error);
|
|
8122
|
-
throw error;
|
|
8123
|
-
}
|
|
8124
|
-
function updateBounds(center, width, height) {
|
|
8125
|
-
const halfWidth = width / 2;
|
|
8126
|
-
const halfHeight = height / 2;
|
|
8127
|
-
minX = Math.min(minX, center.x - halfWidth);
|
|
8128
|
-
minY = Math.min(minY, center.y - halfHeight);
|
|
8129
|
-
maxX = Math.max(maxX, center.x + halfWidth);
|
|
8130
|
-
maxY = Math.max(maxY, center.y + halfHeight);
|
|
8131
|
-
}
|
|
8132
|
-
function updateBoundsToIncludeOutline(outline) {
|
|
8133
|
-
for (const point of outline) {
|
|
8134
|
-
minX = Math.min(minX, point.x);
|
|
8135
|
-
minY = Math.min(minY, point.y);
|
|
8136
|
-
maxX = Math.max(maxX, point.x);
|
|
8137
|
-
maxY = Math.max(maxY, point.y);
|
|
8138
|
-
}
|
|
8139
|
-
}
|
|
8140
|
-
}
|
|
8141
|
-
function createSvgObjects4({ elm, ctx }) {
|
|
8142
|
-
const { transform } = ctx;
|
|
8143
|
-
switch (elm.type) {
|
|
8144
|
-
case "pcb_board":
|
|
8145
|
-
return createSvgObjectsFromPcbBoard(elm, ctx);
|
|
8146
|
-
case "pcb_solder_paste":
|
|
8147
|
-
return createSvgObjectsFromSolderPaste(elm, ctx);
|
|
8148
|
-
default:
|
|
8149
|
-
return [];
|
|
8150
|
-
}
|
|
8151
|
-
}
|
|
8152
|
-
function createSvgObjectFromPcbBoundary2(transform, minX, minY, maxX, maxY) {
|
|
8153
|
-
const [x1, y1] = applyToPoint60(transform, [minX, minY]);
|
|
8154
|
-
const [x2, y2] = applyToPoint60(transform, [maxX, maxY]);
|
|
8155
|
-
const width = Math.abs(x2 - x1);
|
|
8156
|
-
const height = Math.abs(y2 - y1);
|
|
8157
|
-
const x = Math.min(x1, x2);
|
|
8158
|
-
const y = Math.min(y1, y2);
|
|
8159
|
-
return {
|
|
8160
|
-
name: "rect",
|
|
8161
|
-
type: "element",
|
|
8162
|
-
value: "",
|
|
8163
|
-
children: [],
|
|
8164
|
-
attributes: {
|
|
8165
|
-
class: "pcb-boundary",
|
|
8166
|
-
fill: "none",
|
|
8167
|
-
stroke: "#fff",
|
|
8168
|
-
"stroke-width": "0.3",
|
|
8169
|
-
x: x.toString(),
|
|
8170
|
-
y: y.toString(),
|
|
8171
|
-
width: width.toString(),
|
|
8172
|
-
height: height.toString()
|
|
8173
|
-
}
|
|
8174
|
-
};
|
|
8175
|
-
}
|
|
7936
|
+
// lib/convert-circuit-json-to-schematic-simulation-svg.ts
|
|
7937
|
+
import { stringify as stringify6, parseSync as parseSync3 } from "svgson";
|
|
8176
7938
|
|
|
8177
7939
|
// lib/sim/convert-circuit-json-to-simulation-graph-svg.ts
|
|
8178
|
-
import { stringify as
|
|
7940
|
+
import { stringify as stringify5 } from "svgson";
|
|
8179
7941
|
|
|
8180
7942
|
// lib/sim/types.ts
|
|
8181
7943
|
function isSimulationTransientVoltageGraph(value) {
|
|
@@ -8284,7 +8046,7 @@ function convertCircuitJsonToSimulationGraphSvg({
|
|
|
8284
8046
|
},
|
|
8285
8047
|
svgChildren
|
|
8286
8048
|
);
|
|
8287
|
-
return
|
|
8049
|
+
return stringify5(svgObject);
|
|
8288
8050
|
}
|
|
8289
8051
|
function prepareSimulationGraphs(graphs) {
|
|
8290
8052
|
const palette = Array.isArray(colorMap.palette) ? colorMap.palette : [];
|
|
@@ -8702,6 +8464,360 @@ function textNode(value) {
|
|
|
8702
8464
|
children: []
|
|
8703
8465
|
};
|
|
8704
8466
|
}
|
|
8467
|
+
|
|
8468
|
+
// lib/convert-circuit-json-to-schematic-simulation-svg.ts
|
|
8469
|
+
var DEFAULT_WIDTH2 = 1200;
|
|
8470
|
+
var DEFAULT_HEIGHT2 = 1200;
|
|
8471
|
+
var DEFAULT_SCHEMATIC_RATIO = 0.55;
|
|
8472
|
+
function convertCircuitJsonToSchematicSimulationSvg({
|
|
8473
|
+
circuitJson,
|
|
8474
|
+
simulation_experiment_id,
|
|
8475
|
+
simulation_transient_voltage_graph_ids,
|
|
8476
|
+
width = DEFAULT_WIDTH2,
|
|
8477
|
+
height = DEFAULT_HEIGHT2,
|
|
8478
|
+
schematicHeightRatio = DEFAULT_SCHEMATIC_RATIO,
|
|
8479
|
+
schematicOptions,
|
|
8480
|
+
includeVersion
|
|
8481
|
+
}) {
|
|
8482
|
+
const schematicElements = circuitJson.filter(
|
|
8483
|
+
(element) => !isSimulationExperiment(element) && !isSimulationTransientVoltageGraph(element)
|
|
8484
|
+
);
|
|
8485
|
+
const clampedRatio = clamp01(schematicHeightRatio);
|
|
8486
|
+
const rawSchematicHeight = Math.max(1, height * clampedRatio);
|
|
8487
|
+
const rawSimulationHeight = Math.max(1, height - rawSchematicHeight);
|
|
8488
|
+
const totalRawHeight = rawSchematicHeight + rawSimulationHeight;
|
|
8489
|
+
const scale10 = totalRawHeight === 0 ? 1 : height / totalRawHeight;
|
|
8490
|
+
const schematicHeight = rawSchematicHeight * scale10;
|
|
8491
|
+
const simulationHeight = rawSimulationHeight * scale10;
|
|
8492
|
+
const schematicSvg = convertCircuitJsonToSchematicSvg(schematicElements, {
|
|
8493
|
+
...schematicOptions,
|
|
8494
|
+
width,
|
|
8495
|
+
height: schematicHeight,
|
|
8496
|
+
includeVersion: false
|
|
8497
|
+
});
|
|
8498
|
+
const simulationSvg = convertCircuitJsonToSimulationGraphSvg({
|
|
8499
|
+
circuitJson,
|
|
8500
|
+
simulation_experiment_id,
|
|
8501
|
+
simulation_transient_voltage_graph_ids,
|
|
8502
|
+
width,
|
|
8503
|
+
height: simulationHeight,
|
|
8504
|
+
includeVersion: false
|
|
8505
|
+
});
|
|
8506
|
+
const schematicNode = ensureElementNode(parseSync3(schematicSvg));
|
|
8507
|
+
const simulationNode = ensureElementNode(parseSync3(simulationSvg));
|
|
8508
|
+
const combinedChildren = [];
|
|
8509
|
+
combinedChildren.push(
|
|
8510
|
+
translateNestedSvg(schematicNode, 0, 0, width, schematicHeight)
|
|
8511
|
+
);
|
|
8512
|
+
combinedChildren.push(
|
|
8513
|
+
translateNestedSvg(
|
|
8514
|
+
simulationNode,
|
|
8515
|
+
0,
|
|
8516
|
+
schematicHeight,
|
|
8517
|
+
width,
|
|
8518
|
+
simulationHeight
|
|
8519
|
+
)
|
|
8520
|
+
);
|
|
8521
|
+
const softwareUsedString = getSoftwareUsedString(schematicElements);
|
|
8522
|
+
const svgObject = {
|
|
8523
|
+
name: "svg",
|
|
8524
|
+
type: "element",
|
|
8525
|
+
value: "",
|
|
8526
|
+
attributes: {
|
|
8527
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
8528
|
+
width: formatNumber2(width),
|
|
8529
|
+
height: formatNumber2(height),
|
|
8530
|
+
viewBox: `0 0 ${formatNumber2(width)} ${formatNumber2(height)}`,
|
|
8531
|
+
"data-simulation-experiment-id": simulation_experiment_id,
|
|
8532
|
+
...softwareUsedString && {
|
|
8533
|
+
"data-software-used-string": softwareUsedString
|
|
8534
|
+
},
|
|
8535
|
+
...includeVersion && {
|
|
8536
|
+
"data-circuit-to-svg-version": CIRCUIT_TO_SVG_VERSION
|
|
8537
|
+
}
|
|
8538
|
+
},
|
|
8539
|
+
children: combinedChildren
|
|
8540
|
+
};
|
|
8541
|
+
return stringify6(svgObject);
|
|
8542
|
+
}
|
|
8543
|
+
function translateNestedSvg(node, x, y, width, height) {
|
|
8544
|
+
const clone = cloneSvgObject(node);
|
|
8545
|
+
clone.attributes = {
|
|
8546
|
+
...clone.attributes,
|
|
8547
|
+
x: formatNumber2(x),
|
|
8548
|
+
y: formatNumber2(y),
|
|
8549
|
+
width: formatNumber2(width),
|
|
8550
|
+
height: formatNumber2(height)
|
|
8551
|
+
};
|
|
8552
|
+
delete clone.attributes.xmlns;
|
|
8553
|
+
return clone;
|
|
8554
|
+
}
|
|
8555
|
+
function ensureElementNode(node) {
|
|
8556
|
+
if (node.type !== "element") {
|
|
8557
|
+
throw new Error("Expected SVG root element to be of type 'element'");
|
|
8558
|
+
}
|
|
8559
|
+
return node;
|
|
8560
|
+
}
|
|
8561
|
+
function cloneSvgObject(node) {
|
|
8562
|
+
return {
|
|
8563
|
+
...node,
|
|
8564
|
+
attributes: { ...node.attributes ?? {} },
|
|
8565
|
+
children: node.children?.map(cloneSvgObject) ?? []
|
|
8566
|
+
};
|
|
8567
|
+
}
|
|
8568
|
+
function clamp01(value) {
|
|
8569
|
+
if (!Number.isFinite(value)) return DEFAULT_SCHEMATIC_RATIO;
|
|
8570
|
+
if (value <= 0) return 0;
|
|
8571
|
+
if (value >= 1) return 1;
|
|
8572
|
+
return value;
|
|
8573
|
+
}
|
|
8574
|
+
function formatNumber2(value) {
|
|
8575
|
+
if (!Number.isFinite(value)) return "0";
|
|
8576
|
+
const rounded = Number.parseFloat(value.toFixed(6));
|
|
8577
|
+
if (Number.isInteger(rounded)) return rounded.toString();
|
|
8578
|
+
return rounded.toString();
|
|
8579
|
+
}
|
|
8580
|
+
|
|
8581
|
+
// lib/pcb/convert-circuit-json-to-solder-paste-mask.ts
|
|
8582
|
+
import { stringify as stringify7 } from "svgson";
|
|
8583
|
+
import {
|
|
8584
|
+
applyToPoint as applyToPoint60,
|
|
8585
|
+
compose as compose14,
|
|
8586
|
+
scale as scale9,
|
|
8587
|
+
translate as translate14
|
|
8588
|
+
} from "transformation-matrix";
|
|
8589
|
+
|
|
8590
|
+
// lib/pcb/svg-object-fns/convert-circuit-json-to-solder-paste-mask.ts
|
|
8591
|
+
import { applyToPoint as applyToPoint59 } from "transformation-matrix";
|
|
8592
|
+
function createSvgObjectsFromSolderPaste(solderPaste, ctx) {
|
|
8593
|
+
const { transform, layer: layerFilter } = ctx;
|
|
8594
|
+
if (layerFilter && solderPaste.layer !== layerFilter) return [];
|
|
8595
|
+
const [x, y] = applyToPoint59(transform, [solderPaste.x, solderPaste.y]);
|
|
8596
|
+
if (solderPaste.shape === "rect" || solderPaste.shape === "rotated_rect") {
|
|
8597
|
+
const width = solderPaste.width * Math.abs(transform.a);
|
|
8598
|
+
const height = solderPaste.height * Math.abs(transform.d);
|
|
8599
|
+
if (solderPaste.shape === "rotated_rect" && solderPaste.ccw_rotation) {
|
|
8600
|
+
return [
|
|
8601
|
+
{
|
|
8602
|
+
name: "rect",
|
|
8603
|
+
type: "element",
|
|
8604
|
+
attributes: {
|
|
8605
|
+
class: "pcb-solder-paste",
|
|
8606
|
+
fill: solderPasteLayerNameToColor(solderPaste.layer),
|
|
8607
|
+
x: (-width / 2).toString(),
|
|
8608
|
+
y: (-height / 2).toString(),
|
|
8609
|
+
width: width.toString(),
|
|
8610
|
+
height: height.toString(),
|
|
8611
|
+
transform: `translate(${x} ${y}) rotate(${-solderPaste.ccw_rotation})`
|
|
8612
|
+
}
|
|
8613
|
+
}
|
|
8614
|
+
];
|
|
8615
|
+
}
|
|
8616
|
+
return [
|
|
8617
|
+
{
|
|
8618
|
+
name: "rect",
|
|
8619
|
+
type: "element",
|
|
8620
|
+
attributes: {
|
|
8621
|
+
class: "pcb-solder-paste",
|
|
8622
|
+
fill: solderPasteLayerNameToColor(solderPaste.layer),
|
|
8623
|
+
x: (x - width / 2).toString(),
|
|
8624
|
+
y: (y - height / 2).toString(),
|
|
8625
|
+
width: width.toString(),
|
|
8626
|
+
height: height.toString()
|
|
8627
|
+
}
|
|
8628
|
+
}
|
|
8629
|
+
];
|
|
8630
|
+
}
|
|
8631
|
+
if (solderPaste.shape === "pill") {
|
|
8632
|
+
const width = solderPaste.width * Math.abs(transform.a);
|
|
8633
|
+
const height = solderPaste.height * Math.abs(transform.d);
|
|
8634
|
+
const radius = solderPaste.radius * Math.abs(transform.a);
|
|
8635
|
+
return [
|
|
8636
|
+
{
|
|
8637
|
+
name: "rect",
|
|
8638
|
+
type: "element",
|
|
8639
|
+
attributes: {
|
|
8640
|
+
class: "pcb-solder-paste",
|
|
8641
|
+
fill: solderPasteLayerNameToColor(solderPaste.layer),
|
|
8642
|
+
x: (x - width / 2).toString(),
|
|
8643
|
+
y: (y - height / 2).toString(),
|
|
8644
|
+
width: width.toString(),
|
|
8645
|
+
height: height.toString(),
|
|
8646
|
+
rx: radius.toString()
|
|
8647
|
+
}
|
|
8648
|
+
}
|
|
8649
|
+
];
|
|
8650
|
+
}
|
|
8651
|
+
if (solderPaste.shape === "circle") {
|
|
8652
|
+
const radius = solderPaste.radius * Math.abs(transform.a);
|
|
8653
|
+
return [
|
|
8654
|
+
{
|
|
8655
|
+
name: "circle",
|
|
8656
|
+
type: "element",
|
|
8657
|
+
attributes: {
|
|
8658
|
+
class: "pcb-solder-paste",
|
|
8659
|
+
fill: solderPasteLayerNameToColor(solderPaste.layer),
|
|
8660
|
+
cx: x.toString(),
|
|
8661
|
+
cy: y.toString(),
|
|
8662
|
+
r: radius.toString()
|
|
8663
|
+
}
|
|
8664
|
+
}
|
|
8665
|
+
];
|
|
8666
|
+
}
|
|
8667
|
+
}
|
|
8668
|
+
|
|
8669
|
+
// lib/pcb/convert-circuit-json-to-solder-paste-mask.ts
|
|
8670
|
+
var OBJECT_ORDER4 = [
|
|
8671
|
+
"pcb_board",
|
|
8672
|
+
"pcb_solder_paste"
|
|
8673
|
+
];
|
|
8674
|
+
function convertCircuitJsonToSolderPasteMask(circuitJson, options) {
|
|
8675
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
8676
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
8677
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
8678
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
8679
|
+
const filteredCircuitJson = circuitJson.filter(
|
|
8680
|
+
(elm) => elm.type === "pcb_board" || elm.type === "pcb_solder_paste" && elm.layer === options.layer
|
|
8681
|
+
);
|
|
8682
|
+
for (const item of filteredCircuitJson) {
|
|
8683
|
+
if (item.type === "pcb_board") {
|
|
8684
|
+
if (item.outline && Array.isArray(item.outline) && item.outline.length >= 3) {
|
|
8685
|
+
updateBoundsToIncludeOutline(item.outline);
|
|
8686
|
+
} else if ("center" in item && "width" in item && "height" in item) {
|
|
8687
|
+
updateBounds(item.center, item.width, item.height);
|
|
8688
|
+
}
|
|
8689
|
+
} else if (item.type === "pcb_solder_paste" && "x" in item && "y" in item) {
|
|
8690
|
+
updateBounds({ x: item.x, y: item.y }, 0, 0);
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
const padding = 1;
|
|
8694
|
+
const circuitWidth = maxX - minX + 2 * padding;
|
|
8695
|
+
const circuitHeight = maxY - minY + 2 * padding;
|
|
8696
|
+
const svgWidth = options.width ?? 800;
|
|
8697
|
+
const svgHeight = options.height ?? 600;
|
|
8698
|
+
const scaleX = svgWidth / circuitWidth;
|
|
8699
|
+
const scaleY = svgHeight / circuitHeight;
|
|
8700
|
+
const scaleFactor = Math.min(scaleX, scaleY);
|
|
8701
|
+
const offsetX = (svgWidth - circuitWidth * scaleFactor) / 2;
|
|
8702
|
+
const offsetY = (svgHeight - circuitHeight * scaleFactor) / 2;
|
|
8703
|
+
const transform = compose14(
|
|
8704
|
+
translate14(
|
|
8705
|
+
offsetX - minX * scaleFactor + padding * scaleFactor,
|
|
8706
|
+
svgHeight - offsetY + minY * scaleFactor - padding * scaleFactor
|
|
8707
|
+
),
|
|
8708
|
+
scale9(scaleFactor, -scaleFactor)
|
|
8709
|
+
// Flip in y-direction
|
|
8710
|
+
);
|
|
8711
|
+
const ctx = {
|
|
8712
|
+
transform,
|
|
8713
|
+
layer: options.layer,
|
|
8714
|
+
colorMap: DEFAULT_PCB_COLOR_MAP
|
|
8715
|
+
};
|
|
8716
|
+
const svgObjects = filteredCircuitJson.sort(
|
|
8717
|
+
(a, b) => (OBJECT_ORDER4.indexOf(b.type) ?? 9999) - (OBJECT_ORDER4.indexOf(a.type) ?? 9999)
|
|
8718
|
+
).flatMap((item) => createSvgObjects4({ elm: item, ctx }));
|
|
8719
|
+
const softwareUsedString = getSoftwareUsedString(circuitJson);
|
|
8720
|
+
const version = CIRCUIT_TO_SVG_VERSION;
|
|
8721
|
+
const svgObject = {
|
|
8722
|
+
name: "svg",
|
|
8723
|
+
type: "element",
|
|
8724
|
+
attributes: {
|
|
8725
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
8726
|
+
width: svgWidth.toString(),
|
|
8727
|
+
height: svgHeight.toString(),
|
|
8728
|
+
...softwareUsedString && {
|
|
8729
|
+
"data-software-used-string": softwareUsedString
|
|
8730
|
+
},
|
|
8731
|
+
...options.includeVersion && {
|
|
8732
|
+
"data-circuit-to-svg-version": version
|
|
8733
|
+
}
|
|
8734
|
+
},
|
|
8735
|
+
value: "",
|
|
8736
|
+
children: [
|
|
8737
|
+
{
|
|
8738
|
+
name: "style",
|
|
8739
|
+
type: "element",
|
|
8740
|
+
children: [
|
|
8741
|
+
{
|
|
8742
|
+
type: "text",
|
|
8743
|
+
value: ""
|
|
8744
|
+
}
|
|
8745
|
+
]
|
|
8746
|
+
},
|
|
8747
|
+
{
|
|
8748
|
+
name: "rect",
|
|
8749
|
+
type: "element",
|
|
8750
|
+
attributes: {
|
|
8751
|
+
class: "boundary",
|
|
8752
|
+
x: "0",
|
|
8753
|
+
y: "0",
|
|
8754
|
+
fill: "#000",
|
|
8755
|
+
width: svgWidth.toString(),
|
|
8756
|
+
height: svgHeight.toString()
|
|
8757
|
+
}
|
|
8758
|
+
},
|
|
8759
|
+
createSvgObjectFromPcbBoundary2(transform, minX, minY, maxX, maxY),
|
|
8760
|
+
...svgObjects
|
|
8761
|
+
].filter((child) => child !== null)
|
|
8762
|
+
};
|
|
8763
|
+
try {
|
|
8764
|
+
return stringify7(svgObject);
|
|
8765
|
+
} catch (error) {
|
|
8766
|
+
console.error("Error stringifying SVG object:", error);
|
|
8767
|
+
throw error;
|
|
8768
|
+
}
|
|
8769
|
+
function updateBounds(center, width, height) {
|
|
8770
|
+
const halfWidth = width / 2;
|
|
8771
|
+
const halfHeight = height / 2;
|
|
8772
|
+
minX = Math.min(minX, center.x - halfWidth);
|
|
8773
|
+
minY = Math.min(minY, center.y - halfHeight);
|
|
8774
|
+
maxX = Math.max(maxX, center.x + halfWidth);
|
|
8775
|
+
maxY = Math.max(maxY, center.y + halfHeight);
|
|
8776
|
+
}
|
|
8777
|
+
function updateBoundsToIncludeOutline(outline) {
|
|
8778
|
+
for (const point of outline) {
|
|
8779
|
+
minX = Math.min(minX, point.x);
|
|
8780
|
+
minY = Math.min(minY, point.y);
|
|
8781
|
+
maxX = Math.max(maxX, point.x);
|
|
8782
|
+
maxY = Math.max(maxY, point.y);
|
|
8783
|
+
}
|
|
8784
|
+
}
|
|
8785
|
+
}
|
|
8786
|
+
function createSvgObjects4({ elm, ctx }) {
|
|
8787
|
+
const { transform } = ctx;
|
|
8788
|
+
switch (elm.type) {
|
|
8789
|
+
case "pcb_board":
|
|
8790
|
+
return createSvgObjectsFromPcbBoard(elm, ctx);
|
|
8791
|
+
case "pcb_solder_paste":
|
|
8792
|
+
return createSvgObjectsFromSolderPaste(elm, ctx);
|
|
8793
|
+
default:
|
|
8794
|
+
return [];
|
|
8795
|
+
}
|
|
8796
|
+
}
|
|
8797
|
+
function createSvgObjectFromPcbBoundary2(transform, minX, minY, maxX, maxY) {
|
|
8798
|
+
const [x1, y1] = applyToPoint60(transform, [minX, minY]);
|
|
8799
|
+
const [x2, y2] = applyToPoint60(transform, [maxX, maxY]);
|
|
8800
|
+
const width = Math.abs(x2 - x1);
|
|
8801
|
+
const height = Math.abs(y2 - y1);
|
|
8802
|
+
const x = Math.min(x1, x2);
|
|
8803
|
+
const y = Math.min(y1, y2);
|
|
8804
|
+
return {
|
|
8805
|
+
name: "rect",
|
|
8806
|
+
type: "element",
|
|
8807
|
+
value: "",
|
|
8808
|
+
children: [],
|
|
8809
|
+
attributes: {
|
|
8810
|
+
class: "pcb-boundary",
|
|
8811
|
+
fill: "none",
|
|
8812
|
+
stroke: "#fff",
|
|
8813
|
+
"stroke-width": "0.3",
|
|
8814
|
+
x: x.toString(),
|
|
8815
|
+
y: y.toString(),
|
|
8816
|
+
width: width.toString(),
|
|
8817
|
+
height: height.toString()
|
|
8818
|
+
}
|
|
8819
|
+
};
|
|
8820
|
+
}
|
|
8705
8821
|
export {
|
|
8706
8822
|
CIRCUIT_TO_SVG_VERSION,
|
|
8707
8823
|
circuitJsonToPcbSvg,
|
|
@@ -8709,6 +8825,7 @@ export {
|
|
|
8709
8825
|
convertCircuitJsonToAssemblySvg,
|
|
8710
8826
|
convertCircuitJsonToPcbSvg,
|
|
8711
8827
|
convertCircuitJsonToPinoutSvg,
|
|
8828
|
+
convertCircuitJsonToSchematicSimulationSvg,
|
|
8712
8829
|
convertCircuitJsonToSchematicSvg,
|
|
8713
8830
|
convertCircuitJsonToSimulationGraphSvg,
|
|
8714
8831
|
convertCircuitJsonToSolderPasteMask,
|