circuit-to-svg 0.0.205 → 0.0.207

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 CHANGED
@@ -293,6 +293,38 @@ interface Options {
293
293
  }
294
294
  declare function convertCircuitJsonToSolderPasteMask(circuitJson: AnyCircuitElement[], options: Options): string;
295
295
 
296
+ type ExperimentType = string;
297
+ interface SimulationExperimentElement {
298
+ type: "simulation_experiment";
299
+ simulation_experiment_id: string;
300
+ name: string;
301
+ experiment_type: ExperimentType;
302
+ }
303
+ interface SimulationTransientVoltageGraphElement {
304
+ type: "simulation_transient_voltage_graph";
305
+ simulation_transient_voltage_graph_id: string;
306
+ simulation_experiment_id: string;
307
+ timestamps_ms?: number[];
308
+ voltage_levels: number[];
309
+ schematic_voltage_probe_id?: string;
310
+ subcircuit_connecivity_map_key?: string;
311
+ time_per_step: number;
312
+ start_time_ms: number;
313
+ end_time_ms: number;
314
+ name?: string;
315
+ }
316
+ type CircuitJsonWithSimulation = AnyCircuitElement | SimulationExperimentElement | SimulationTransientVoltageGraphElement;
317
+
318
+ interface ConvertSimulationGraphParams {
319
+ circuitJson: CircuitJsonWithSimulation[];
320
+ simulation_experiment_id: string;
321
+ simulation_transient_voltage_graph_ids?: string[];
322
+ width?: number;
323
+ height?: number;
324
+ includeVersion?: boolean;
325
+ }
326
+ declare function convertCircuitJsonToSimulationGraphSvg({ circuitJson, simulation_experiment_id, simulation_transient_voltage_graph_ids, width, height, includeVersion, }: ConvertSimulationGraphParams): string;
327
+
296
328
  declare function getSoftwareUsedString(circuitJson: AnyCircuitElement[]): string | undefined;
297
329
 
298
330
  declare const CIRCUIT_TO_SVG_VERSION: string;
@@ -303,4 +335,4 @@ declare const createSvgObjectsForSchComponentPortHovers: ({ component, transform
303
335
  circuitJson: AnyCircuitElement[];
304
336
  }) => INode[];
305
337
 
306
- 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, convertCircuitJsonToSolderPasteMask, createSvgObjectsForSchComponentPortHovers, getSoftwareUsedString };
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 };
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.204",
1786
+ version: "0.0.206",
1787
1787
  description: "Convert Circuit JSON to SVG",
1788
1788
  main: "dist/index.js",
1789
1789
  files: [
@@ -1807,7 +1807,7 @@ var package_default = {
1807
1807
  "bun-match-svg": "^0.0.12",
1808
1808
  esbuild: "^0.20.2",
1809
1809
  "performance-now": "^2.1.0",
1810
- "circuit-json": "^0.0.261",
1810
+ "circuit-json": "^0.0.267",
1811
1811
  react: "19.1.0",
1812
1812
  "react-cosmos": "7.0.0",
1813
1813
  "react-cosmos-plugin-vite": "7.0.0",
@@ -8173,6 +8173,535 @@ function createSvgObjectFromPcbBoundary2(transform, minX, minY, maxX, maxY) {
8173
8173
  }
8174
8174
  };
8175
8175
  }
8176
+
8177
+ // lib/sim/convert-circuit-json-to-simulation-graph-svg.ts
8178
+ import { stringify as stringify6 } from "svgson";
8179
+
8180
+ // lib/sim/types.ts
8181
+ function isSimulationTransientVoltageGraph(value) {
8182
+ return value?.type === "simulation_transient_voltage_graph";
8183
+ }
8184
+ function isSimulationExperiment(value) {
8185
+ return value?.type === "simulation_experiment";
8186
+ }
8187
+
8188
+ // lib/sim/convert-circuit-json-to-simulation-graph-svg.ts
8189
+ var DEFAULT_WIDTH = 1200;
8190
+ var DEFAULT_HEIGHT = 600;
8191
+ var MARGIN = { top: 64, right: 48, bottom: 80, left: 100 };
8192
+ var FALLBACK_LINE_COLOR = "#1f77b4";
8193
+ function convertCircuitJsonToSimulationGraphSvg({
8194
+ circuitJson,
8195
+ simulation_experiment_id,
8196
+ simulation_transient_voltage_graph_ids,
8197
+ width = DEFAULT_WIDTH,
8198
+ height = DEFAULT_HEIGHT,
8199
+ includeVersion
8200
+ }) {
8201
+ const selectedIds = simulation_transient_voltage_graph_ids ? new Set(simulation_transient_voltage_graph_ids) : null;
8202
+ const experiment = circuitJson.find(
8203
+ (element) => isSimulationExperiment(element) && element.simulation_experiment_id === simulation_experiment_id
8204
+ );
8205
+ const graphs = circuitJson.filter(
8206
+ (element) => isSimulationTransientVoltageGraph(element) && element.simulation_experiment_id === simulation_experiment_id && (!selectedIds || selectedIds.has(element.simulation_transient_voltage_graph_id))
8207
+ );
8208
+ if (graphs.length === 0) {
8209
+ throw new Error(
8210
+ `No simulation_transient_voltage_graph elements found for simulation_experiment_id "${simulation_experiment_id}"`
8211
+ );
8212
+ }
8213
+ const preparedGraphs = prepareSimulationGraphs(graphs);
8214
+ const allPoints = preparedGraphs.flatMap((entry) => entry.points);
8215
+ if (allPoints.length === 0) {
8216
+ throw new Error(
8217
+ `simulation_transient_voltage_graph elements for simulation_experiment_id "${simulation_experiment_id}" do not contain any datapoints`
8218
+ );
8219
+ }
8220
+ const timeAxis = buildAxisInfo(allPoints.map((point) => point.timeMs));
8221
+ const voltageAxis = buildAxisInfo(allPoints.map((point) => point.voltage));
8222
+ const plotWidth = Math.max(1, width - MARGIN.left - MARGIN.right);
8223
+ const plotHeight = Math.max(1, height - MARGIN.top - MARGIN.bottom);
8224
+ const scaleX = createLinearScale(
8225
+ timeAxis.domainMin,
8226
+ timeAxis.domainMax,
8227
+ MARGIN.left,
8228
+ MARGIN.left + plotWidth
8229
+ );
8230
+ const scaleY = createLinearScale(
8231
+ voltageAxis.domainMin,
8232
+ voltageAxis.domainMax,
8233
+ MARGIN.top + plotHeight,
8234
+ MARGIN.top
8235
+ );
8236
+ const clipPathId = createClipPathId(simulation_experiment_id);
8237
+ const softwareUsedString = getSoftwareUsedString(
8238
+ circuitJson
8239
+ );
8240
+ const version = CIRCUIT_TO_SVG_VERSION;
8241
+ const titleNode = createTitleNode(experiment, width);
8242
+ const svgChildren = [
8243
+ createStyleNode(),
8244
+ createBackgroundRect(width, height),
8245
+ createDefsNode(clipPathId, plotWidth, plotHeight),
8246
+ createPlotBackground(plotWidth, plotHeight),
8247
+ createGridLines({
8248
+ timeAxis,
8249
+ voltageAxis,
8250
+ scaleX,
8251
+ scaleY,
8252
+ plotWidth,
8253
+ plotHeight
8254
+ }),
8255
+ createDataGroup(preparedGraphs, clipPathId, scaleX, scaleY),
8256
+ createAxes({
8257
+ timeAxis,
8258
+ voltageAxis,
8259
+ scaleX,
8260
+ scaleY,
8261
+ plotWidth,
8262
+ plotHeight
8263
+ }),
8264
+ createLegend(preparedGraphs),
8265
+ ...titleNode ? [titleNode] : []
8266
+ ];
8267
+ const svgObject = svgElement(
8268
+ "svg",
8269
+ {
8270
+ xmlns: "http://www.w3.org/2000/svg",
8271
+ width: width.toString(),
8272
+ height: height.toString(),
8273
+ viewBox: `0 0 ${formatNumber(width)} ${formatNumber(height)}`,
8274
+ "data-simulation-experiment-id": simulation_experiment_id,
8275
+ ...experiment?.name && {
8276
+ "data-simulation-experiment-name": experiment.name
8277
+ },
8278
+ ...softwareUsedString && {
8279
+ "data-software-used-string": softwareUsedString
8280
+ },
8281
+ ...includeVersion && {
8282
+ "data-circuit-to-svg-version": version
8283
+ }
8284
+ },
8285
+ svgChildren
8286
+ );
8287
+ return stringify6(svgObject);
8288
+ }
8289
+ function prepareSimulationGraphs(graphs) {
8290
+ const palette = Array.isArray(colorMap.palette) ? colorMap.palette : [];
8291
+ return graphs.map((graph, index) => {
8292
+ const points = createGraphPoints(graph);
8293
+ const paletteColor = palette.length > 0 ? palette[index % palette.length] : FALLBACK_LINE_COLOR;
8294
+ const color = paletteColor ?? FALLBACK_LINE_COLOR;
8295
+ const label = graph.name || (graph.schematic_voltage_probe_id ? `Probe ${graph.schematic_voltage_probe_id}` : graph.simulation_transient_voltage_graph_id);
8296
+ return { graph, points, color, label };
8297
+ }).filter((entry) => entry.points.length > 0);
8298
+ }
8299
+ function createGraphPoints(graph) {
8300
+ const timestamps = getTimestamps(graph);
8301
+ const length = Math.min(timestamps.length, graph.voltage_levels.length);
8302
+ const points = [];
8303
+ for (let index = 0; index < length; index++) {
8304
+ const timeMs = Number(timestamps[index] ?? Number.NaN);
8305
+ const voltage = Number(graph.voltage_levels[index] ?? Number.NaN);
8306
+ if (!Number.isFinite(timeMs) || !Number.isFinite(voltage)) continue;
8307
+ points.push({ timeMs, voltage });
8308
+ }
8309
+ return points;
8310
+ }
8311
+ function getTimestamps(graph) {
8312
+ if (Array.isArray(graph.timestamps_ms) && graph.timestamps_ms.length === graph.voltage_levels.length) {
8313
+ return graph.timestamps_ms.map((value) => Number(value));
8314
+ }
8315
+ const count = graph.voltage_levels.length;
8316
+ if (count === 0) return [];
8317
+ const timestamps = [];
8318
+ for (let index = 0; index < count; index++) {
8319
+ timestamps.push(graph.start_time_ms + graph.time_per_step * index);
8320
+ }
8321
+ const lastTimestamp = timestamps.length > 0 ? timestamps[timestamps.length - 1] : void 0;
8322
+ if (lastTimestamp !== void 0 && Number.isFinite(graph.end_time_ms) && Number.isFinite(lastTimestamp) && Math.abs(lastTimestamp - graph.end_time_ms) > graph.time_per_step / 2) {
8323
+ timestamps.push(graph.end_time_ms);
8324
+ }
8325
+ return timestamps;
8326
+ }
8327
+ function buildAxisInfo(values) {
8328
+ if (values.length === 0) {
8329
+ return {
8330
+ domainMin: 0,
8331
+ domainMax: 1,
8332
+ ticks: [0, 1]
8333
+ };
8334
+ }
8335
+ const min = Math.min(...values);
8336
+ const max = Math.max(...values);
8337
+ if (min === max) {
8338
+ const offset = min === 0 ? 1 : Math.abs(min) * 0.1 || 1;
8339
+ return {
8340
+ domainMin: min - offset,
8341
+ domainMax: min + offset,
8342
+ ticks: [min - offset, min, min + offset]
8343
+ };
8344
+ }
8345
+ const ticks = generateTickValues(min, max);
8346
+ const safeTicks = ticks.length > 0 ? ticks : [min, max];
8347
+ const domainMin = safeTicks[0];
8348
+ const domainMax = safeTicks[safeTicks.length - 1];
8349
+ return { domainMin, domainMax, ticks: safeTicks };
8350
+ }
8351
+ function generateTickValues(min, max, desired = 6) {
8352
+ const span = max - min;
8353
+ if (!Number.isFinite(span) || span <= Number.EPSILON) {
8354
+ return [min, max];
8355
+ }
8356
+ const step = niceStep(span / Math.max(1, desired - 1));
8357
+ const niceMin = Math.floor(min / step) * step;
8358
+ const niceMax = Math.ceil(max / step) * step;
8359
+ const values = [];
8360
+ for (let value = niceMin; value <= niceMax + step / 2; value += step) {
8361
+ values.push(Number.parseFloat(value.toPrecision(12)));
8362
+ }
8363
+ return values;
8364
+ }
8365
+ function niceStep(step) {
8366
+ if (!Number.isFinite(step) || step <= 0) return 1;
8367
+ const exponent = Math.floor(Math.log10(step));
8368
+ const fraction = step / Math.pow(10, exponent);
8369
+ let niceFraction;
8370
+ if (fraction <= 1) niceFraction = 1;
8371
+ else if (fraction <= 2) niceFraction = 2;
8372
+ else if (fraction <= 5) niceFraction = 5;
8373
+ else niceFraction = 10;
8374
+ return niceFraction * Math.pow(10, exponent);
8375
+ }
8376
+ function createLinearScale(domainMin, domainMax, rangeMin, rangeMax) {
8377
+ if (!Number.isFinite(domainMin) || !Number.isFinite(domainMax)) {
8378
+ const midpoint = (rangeMin + rangeMax) / 2;
8379
+ return () => midpoint;
8380
+ }
8381
+ const span = domainMax - domainMin;
8382
+ if (Math.abs(span) < Number.EPSILON) {
8383
+ const midpoint = (rangeMin + rangeMax) / 2;
8384
+ return () => midpoint;
8385
+ }
8386
+ return (value) => rangeMin + (value - domainMin) / span * (rangeMax - rangeMin);
8387
+ }
8388
+ function createStyleNode() {
8389
+ const content = `
8390
+ :root { color-scheme: light; }
8391
+ svg { font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif; }
8392
+ .background { fill: ${colorMap.schematic.background}; }
8393
+ .plot-background { fill: #ffffff; }
8394
+ .grid-line { stroke: rgba(0, 0, 0, 0.08); stroke-width: 1; }
8395
+ .axis { stroke: rgba(0, 0, 0, 0.6); stroke-width: 1.5; }
8396
+ .axis-tick { stroke: rgba(0, 0, 0, 0.6); stroke-width: 1; }
8397
+ .axis-label { fill: rgba(0, 0, 0, 0.75); font-size: 12px; }
8398
+ .axis-title { fill: rgba(0, 0, 0, 0.9); font-size: 14px; font-weight: 600; }
8399
+ .legend-label { fill: rgba(0, 0, 0, 0.75); font-size: 13px; }
8400
+ .legend-line { stroke-width: 3; }
8401
+ .simulation-line { fill: none; stroke-width: 2.5; }
8402
+ .simulation-point { stroke-width: 1.5; fill: #ffffff; }
8403
+ .chart-title { fill: rgba(0, 0, 0, 0.85); font-size: 18px; font-weight: 600; }
8404
+ `;
8405
+ return svgElement("style", {}, [textNode(content)]);
8406
+ }
8407
+ function createBackgroundRect(width, height) {
8408
+ return svgElement("rect", {
8409
+ class: "background",
8410
+ x: "0",
8411
+ y: "0",
8412
+ width: formatNumber(width),
8413
+ height: formatNumber(height)
8414
+ });
8415
+ }
8416
+ function createDefsNode(clipPathId, plotWidth, plotHeight) {
8417
+ return svgElement("defs", {}, [
8418
+ svgElement("clipPath", { id: clipPathId }, [
8419
+ svgElement("rect", {
8420
+ x: formatNumber(MARGIN.left),
8421
+ y: formatNumber(MARGIN.top),
8422
+ width: formatNumber(plotWidth),
8423
+ height: formatNumber(plotHeight)
8424
+ })
8425
+ ])
8426
+ ]);
8427
+ }
8428
+ function createPlotBackground(plotWidth, plotHeight) {
8429
+ return svgElement("rect", {
8430
+ class: "plot-background",
8431
+ x: formatNumber(MARGIN.left),
8432
+ y: formatNumber(MARGIN.top),
8433
+ width: formatNumber(plotWidth),
8434
+ height: formatNumber(plotHeight)
8435
+ });
8436
+ }
8437
+ function createGridLines({
8438
+ timeAxis,
8439
+ voltageAxis,
8440
+ scaleX,
8441
+ scaleY,
8442
+ plotWidth,
8443
+ plotHeight
8444
+ }) {
8445
+ const top = MARGIN.top;
8446
+ const bottom = MARGIN.top + plotHeight;
8447
+ const left = MARGIN.left;
8448
+ const right = MARGIN.left + plotWidth;
8449
+ const children = [];
8450
+ for (const tick of timeAxis.ticks) {
8451
+ const x = formatNumber(scaleX(tick));
8452
+ children.push(
8453
+ svgElement("line", {
8454
+ class: "grid-line grid-line-x",
8455
+ x1: x,
8456
+ y1: formatNumber(top),
8457
+ x2: x,
8458
+ y2: formatNumber(bottom)
8459
+ })
8460
+ );
8461
+ }
8462
+ for (const tick of voltageAxis.ticks) {
8463
+ const y = formatNumber(scaleY(tick));
8464
+ children.push(
8465
+ svgElement("line", {
8466
+ class: "grid-line grid-line-y",
8467
+ x1: formatNumber(left),
8468
+ y1: y,
8469
+ x2: formatNumber(right),
8470
+ y2: y
8471
+ })
8472
+ );
8473
+ }
8474
+ return svgElement("g", { class: "grid" }, children);
8475
+ }
8476
+ function createAxes({
8477
+ timeAxis,
8478
+ voltageAxis,
8479
+ scaleX,
8480
+ scaleY,
8481
+ plotWidth,
8482
+ plotHeight
8483
+ }) {
8484
+ const bottom = MARGIN.top + plotHeight;
8485
+ const left = MARGIN.left;
8486
+ const right = MARGIN.left + plotWidth;
8487
+ const children = [
8488
+ svgElement("line", {
8489
+ class: "axis axis-x",
8490
+ x1: formatNumber(left),
8491
+ y1: formatNumber(bottom),
8492
+ x2: formatNumber(right),
8493
+ y2: formatNumber(bottom)
8494
+ }),
8495
+ svgElement("line", {
8496
+ class: "axis axis-y",
8497
+ x1: formatNumber(left),
8498
+ y1: formatNumber(MARGIN.top),
8499
+ x2: formatNumber(left),
8500
+ y2: formatNumber(bottom)
8501
+ })
8502
+ ];
8503
+ for (const tick of timeAxis.ticks) {
8504
+ const x = formatNumber(scaleX(tick));
8505
+ children.push(
8506
+ svgElement("line", {
8507
+ class: "axis-tick axis-tick-x",
8508
+ x1: x,
8509
+ y1: formatNumber(bottom),
8510
+ x2: x,
8511
+ y2: formatNumber(bottom + 6)
8512
+ })
8513
+ );
8514
+ children.push(
8515
+ svgElement(
8516
+ "text",
8517
+ {
8518
+ class: "axis-label axis-label-x",
8519
+ x,
8520
+ y: formatNumber(bottom + 22),
8521
+ "text-anchor": "middle"
8522
+ },
8523
+ [textNode(formatTickLabel(tick, timeAxis.ticks))]
8524
+ )
8525
+ );
8526
+ }
8527
+ for (const tick of voltageAxis.ticks) {
8528
+ const y = formatNumber(scaleY(tick));
8529
+ children.push(
8530
+ svgElement("line", {
8531
+ class: "axis-tick axis-tick-y",
8532
+ x1: formatNumber(left - 6),
8533
+ y1: y,
8534
+ x2: formatNumber(left),
8535
+ y2: y
8536
+ })
8537
+ );
8538
+ children.push(
8539
+ svgElement(
8540
+ "text",
8541
+ {
8542
+ class: "axis-label axis-label-y",
8543
+ x: formatNumber(left - 10),
8544
+ y,
8545
+ "text-anchor": "end",
8546
+ "dominant-baseline": "middle"
8547
+ },
8548
+ [textNode(formatTickLabel(tick, voltageAxis.ticks))]
8549
+ )
8550
+ );
8551
+ }
8552
+ children.push(
8553
+ svgElement(
8554
+ "text",
8555
+ {
8556
+ class: "axis-title axis-title-x",
8557
+ x: formatNumber(left + plotWidth / 2),
8558
+ y: formatNumber(bottom + 48),
8559
+ "text-anchor": "middle"
8560
+ },
8561
+ [textNode("Time (ms)")]
8562
+ ),
8563
+ svgElement(
8564
+ "text",
8565
+ {
8566
+ class: "axis-title axis-title-y",
8567
+ x: formatNumber(left - 64),
8568
+ y: formatNumber(MARGIN.top + plotHeight / 2),
8569
+ transform: `rotate(-90 ${formatNumber(left - 64)} ${formatNumber(
8570
+ MARGIN.top + plotHeight / 2
8571
+ )})`,
8572
+ "text-anchor": "middle"
8573
+ },
8574
+ [textNode("Voltage (V)")]
8575
+ )
8576
+ );
8577
+ return svgElement("g", { class: "axes" }, children);
8578
+ }
8579
+ function createLegend(graphs) {
8580
+ const children = graphs.map((entry, index) => {
8581
+ const y = MARGIN.top - 28 + index * 20;
8582
+ return svgElement(
8583
+ "g",
8584
+ {
8585
+ class: "legend-item",
8586
+ transform: `translate(${formatNumber(MARGIN.left)} ${formatNumber(y)})`
8587
+ },
8588
+ [
8589
+ svgElement("line", {
8590
+ class: "legend-line",
8591
+ x1: "0",
8592
+ y1: "0",
8593
+ x2: "24",
8594
+ y2: "0",
8595
+ stroke: entry.color
8596
+ }),
8597
+ svgElement(
8598
+ "text",
8599
+ {
8600
+ class: "legend-label",
8601
+ x: "32",
8602
+ y: "0",
8603
+ "dominant-baseline": "middle"
8604
+ },
8605
+ [textNode(entry.label)]
8606
+ )
8607
+ ]
8608
+ );
8609
+ });
8610
+ return svgElement("g", { class: "legend" }, children);
8611
+ }
8612
+ function createDataGroup(graphs, clipPathId, scaleX, scaleY) {
8613
+ const elements = [];
8614
+ for (const entry of graphs) {
8615
+ if (entry.points.length === 0) continue;
8616
+ const commands = [];
8617
+ entry.points.forEach((point, index) => {
8618
+ const x = formatNumber(scaleX(point.timeMs));
8619
+ const y = formatNumber(scaleY(point.voltage));
8620
+ commands.push(`${index === 0 ? "M" : "L"} ${x} ${y}`);
8621
+ });
8622
+ const pathAttributes = {
8623
+ class: "simulation-line",
8624
+ d: commands.join(" "),
8625
+ stroke: entry.color,
8626
+ "clip-path": `url(#${clipPathId})`,
8627
+ "data-simulation-transient-voltage-graph-id": entry.graph.simulation_transient_voltage_graph_id
8628
+ };
8629
+ if (entry.graph.schematic_voltage_probe_id) {
8630
+ pathAttributes["data-schematic-voltage-probe-id"] = entry.graph.schematic_voltage_probe_id;
8631
+ }
8632
+ if (entry.graph.subcircuit_connecivity_map_key) {
8633
+ pathAttributes["data-subcircuit-connectivity-map-key"] = entry.graph.subcircuit_connecivity_map_key;
8634
+ }
8635
+ elements.push(svgElement("path", pathAttributes));
8636
+ entry.points.forEach((point) => {
8637
+ const cx = formatNumber(scaleX(point.timeMs));
8638
+ const cy = formatNumber(scaleY(point.voltage));
8639
+ elements.push(
8640
+ svgElement("circle", {
8641
+ class: "simulation-point",
8642
+ cx,
8643
+ cy,
8644
+ r: "3.5",
8645
+ stroke: entry.color,
8646
+ fill: "#ffffff",
8647
+ "clip-path": `url(#${clipPathId})`
8648
+ })
8649
+ );
8650
+ });
8651
+ }
8652
+ return svgElement("g", { class: "data-series" }, elements);
8653
+ }
8654
+ function createTitleNode(experiment, width) {
8655
+ if (!experiment?.name) return null;
8656
+ return svgElement(
8657
+ "text",
8658
+ {
8659
+ class: "chart-title",
8660
+ x: formatNumber(width / 2),
8661
+ y: formatNumber(MARGIN.top - 40),
8662
+ "text-anchor": "middle"
8663
+ },
8664
+ [textNode(experiment.name)]
8665
+ );
8666
+ }
8667
+ function createClipPathId(simulationExperimentId) {
8668
+ const sanitized = simulationExperimentId.replace(/[^a-zA-Z0-9_-]+/g, "-");
8669
+ return `simulation-graph-${sanitized}`;
8670
+ }
8671
+ function formatNumber(value) {
8672
+ if (!Number.isFinite(value)) return "0";
8673
+ const rounded = Number.parseFloat(value.toFixed(6));
8674
+ if (Number.isInteger(rounded)) return rounded.toString();
8675
+ return rounded.toString();
8676
+ }
8677
+ function formatTickLabel(value, ticks) {
8678
+ if (ticks.length <= 1) return formatNumber(value);
8679
+ const span = ticks[ticks.length - 1] - ticks[0];
8680
+ if (!Number.isFinite(span) || span === 0) return formatNumber(value);
8681
+ const precision = span >= 100 ? 0 : span >= 10 ? 1 : span >= 1 ? 2 : 3;
8682
+ const factor = Math.pow(10, precision);
8683
+ const rounded = Math.round(value * factor) / factor;
8684
+ const fixed = rounded.toFixed(precision);
8685
+ return fixed.replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "");
8686
+ }
8687
+ function svgElement(name, attributes, children = []) {
8688
+ return {
8689
+ name,
8690
+ type: "element",
8691
+ value: "",
8692
+ attributes,
8693
+ children
8694
+ };
8695
+ }
8696
+ function textNode(value) {
8697
+ return {
8698
+ name: "",
8699
+ type: "text",
8700
+ value,
8701
+ attributes: {},
8702
+ children: []
8703
+ };
8704
+ }
8176
8705
  export {
8177
8706
  CIRCUIT_TO_SVG_VERSION,
8178
8707
  circuitJsonToPcbSvg,
@@ -8181,6 +8710,7 @@ export {
8181
8710
  convertCircuitJsonToPcbSvg,
8182
8711
  convertCircuitJsonToPinoutSvg,
8183
8712
  convertCircuitJsonToSchematicSvg,
8713
+ convertCircuitJsonToSimulationGraphSvg,
8184
8714
  convertCircuitJsonToSolderPasteMask,
8185
8715
  createSvgObjectsForSchComponentPortHovers,
8186
8716
  getSoftwareUsedString