circuit-to-svg 0.0.49 → 0.0.51

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.js CHANGED
@@ -976,82 +976,55 @@ var colorMap = {
976
976
  }
977
977
  };
978
978
 
979
- // lib/sch/get-schematic-bounds-from-circuit-json.ts
980
- function getSchematicBoundsFromCircuitJson(soup, padding = 0.5) {
981
- let minX = Number.POSITIVE_INFINITY;
982
- let minY = Number.POSITIVE_INFINITY;
983
- let maxX = Number.NEGATIVE_INFINITY;
984
- let maxY = Number.NEGATIVE_INFINITY;
985
- const portSize = 0.2;
986
- for (const item of soup) {
987
- if (item.type === "schematic_component") {
988
- updateBounds(item.center, item.size, item.rotation || 0);
989
- } else if (item.type === "schematic_port") {
990
- updateBounds(item.center, { width: portSize, height: portSize }, 0);
991
- } else if (item.type === "schematic_debug_object") {
992
- if (item.shape === "rect") {
993
- updateBounds(item.center, item.size, 0);
994
- } else if (item.shape === "line") {
995
- updateBounds(item.start, { width: 0.1, height: 0.1 }, 0);
996
- updateBounds(item.end, { width: 0.1, height: 0.1 }, 0);
997
- }
998
- }
999
- }
1000
- minX -= padding;
1001
- minY -= padding;
1002
- maxX += padding;
1003
- maxY += padding;
1004
- return { minX, minY, maxX, maxY };
1005
- function updateBounds(center, size, rotation) {
1006
- const corners = [
1007
- { x: -size.width / 2, y: -size.height / 2 },
1008
- { x: size.width / 2, y: -size.height / 2 },
1009
- { x: size.width / 2, y: size.height / 2 },
1010
- { x: -size.width / 2, y: size.height / 2 }
1011
- ];
1012
- for (const corner of corners) {
1013
- const rotatedX = corner.x * Math.cos(rotation) - corner.y * Math.sin(rotation) + center.x;
1014
- const rotatedY = corner.x * Math.sin(rotation) + corner.y * Math.cos(rotation) + center.y;
1015
- minX = Math.min(minX, rotatedX);
1016
- minY = Math.min(minY, rotatedY);
1017
- maxX = Math.max(maxX, rotatedX);
1018
- maxY = Math.max(maxY, rotatedY);
1019
- }
1020
- }
1021
- }
979
+ // lib/sch/convert-circuit-json-to-schematic-svg.ts
980
+ import { stringify as stringify2 } from "svgson";
981
+ import {
982
+ compose as compose4,
983
+ scale as scale2,
984
+ translate as translate4
985
+ } from "transformation-matrix";
1022
986
 
1023
987
  // lib/sch/draw-schematic-grid.ts
988
+ import { applyToPoint as applyToPoint12 } from "transformation-matrix";
1024
989
  function drawSchematicGrid(params) {
1025
990
  const { minX, minY, maxX, maxY } = params.bounds;
1026
991
  const cellSize = params.cellSize ?? 1;
1027
992
  const labelCells = params.labelCells ?? false;
1028
993
  const gridLines = [];
994
+ const transformPoint = (x, y) => {
995
+ const [transformedX, transformedY] = applyToPoint12(params.transform, [x, y]);
996
+ return { x: transformedX, y: transformedY };
997
+ };
1029
998
  for (let x = Math.ceil(minX); x <= Math.floor(maxX); x += cellSize) {
999
+ const start = transformPoint(x, minY);
1000
+ const end = transformPoint(x, maxY);
1030
1001
  gridLines.push({
1031
1002
  name: "line",
1032
1003
  type: "element",
1033
1004
  attributes: {
1034
- x1: x.toString(),
1035
- y1: minY.toString(),
1036
- x2: x.toString(),
1037
- y2: maxY.toString(),
1005
+ x1: start.x.toString(),
1006
+ y1: start.y.toString(),
1007
+ x2: end.x.toString(),
1008
+ y2: end.y.toString(),
1038
1009
  stroke: colorMap.schematic.grid,
1039
- "stroke-width": "0.01",
1010
+ "stroke-width": (0.01 * Math.abs(params.transform.a)).toString(),
1040
1011
  "stroke-opacity": "0.5"
1041
1012
  }
1042
1013
  });
1043
1014
  }
1044
1015
  for (let y = Math.ceil(minY); y <= Math.floor(maxY); y += cellSize) {
1016
+ const start = transformPoint(minX, y);
1017
+ const end = transformPoint(maxX, y);
1045
1018
  gridLines.push({
1046
1019
  name: "line",
1047
1020
  type: "element",
1048
1021
  attributes: {
1049
- x1: minX.toString(),
1050
- y1: y.toString(),
1051
- x2: maxX.toString(),
1052
- y2: y.toString(),
1022
+ x1: start.x.toString(),
1023
+ y1: start.y.toString(),
1024
+ x2: end.x.toString(),
1025
+ y2: end.y.toString(),
1053
1026
  stroke: colorMap.schematic.grid,
1054
- "stroke-width": "0.01",
1027
+ "stroke-width": (0.01 * Math.abs(params.transform.a)).toString(),
1055
1028
  "stroke-opacity": "0.5"
1056
1029
  }
1057
1030
  });
@@ -1059,15 +1032,18 @@ function drawSchematicGrid(params) {
1059
1032
  if (labelCells) {
1060
1033
  for (let x = Math.ceil(minX); x <= Math.floor(maxX); x += cellSize) {
1061
1034
  for (let y = Math.ceil(minY); y <= Math.floor(maxY); y += cellSize) {
1035
+ const point = transformPoint(x, y);
1062
1036
  gridLines.push({
1063
1037
  name: "text",
1064
1038
  type: "element",
1065
1039
  attributes: {
1066
- x: x.toString(),
1067
- y: y.toString(),
1040
+ x: point.x.toString(),
1041
+ y: point.y.toString(),
1068
1042
  fill: colorMap.schematic.grid,
1069
- "font-size": (cellSize / 6).toString(),
1070
- "fill-opacity": "0.5"
1043
+ "font-size": (cellSize / 6 * Math.abs(params.transform.a)).toString(),
1044
+ "fill-opacity": "0.5",
1045
+ "text-anchor": "middle",
1046
+ "dominant-baseline": "middle"
1071
1047
  },
1072
1048
  children: [
1073
1049
  {
@@ -1092,29 +1068,40 @@ function drawSchematicGrid(params) {
1092
1068
  }
1093
1069
 
1094
1070
  // lib/sch/draw-schematic-labeled-points.ts
1071
+ import { applyToPoint as applyToPoint13 } from "transformation-matrix";
1095
1072
  function drawSchematicLabeledPoints(params) {
1096
1073
  const { points, transform } = params;
1097
1074
  const labeledPointsGroup = [];
1098
1075
  for (const point of points) {
1076
+ const [x1, y1] = applyToPoint13(transform, [point.x - 0.1, -(point.y - 0.1)]);
1077
+ const [x2, y2] = applyToPoint13(transform, [point.x + 0.1, -(point.y + 0.1)]);
1078
+ const [x3, y3] = applyToPoint13(transform, [point.x - 0.1, -(point.y + 0.1)]);
1079
+ const [x4, y4] = applyToPoint13(transform, [point.x + 0.1, -(point.y - 0.1)]);
1099
1080
  labeledPointsGroup.push({
1100
1081
  name: "path",
1101
1082
  type: "element",
1102
1083
  attributes: {
1103
- d: `M${point.x - 0.1},${point.y - 0.1} L${point.x + 0.1},${point.y + 0.1} M${point.x - 0.1},${point.y + 0.1} L${point.x + 0.1},${point.y - 0.1}`,
1084
+ d: `M${x1},${y1} L${x2},${y2} M${x3},${y3} L${x4},${y4}`,
1104
1085
  stroke: colorMap.schematic.grid,
1105
- "stroke-width": "0.02",
1086
+ "stroke-width": (0.02 * Math.abs(transform.a)).toString(),
1106
1087
  "stroke-opacity": "0.7"
1107
1088
  }
1108
1089
  });
1090
+ const [labelX, labelY] = applyToPoint13(transform, [
1091
+ point.x + 0.15,
1092
+ -(point.y - 0.15)
1093
+ ]);
1109
1094
  labeledPointsGroup.push({
1110
1095
  name: "text",
1111
1096
  type: "element",
1112
1097
  attributes: {
1113
- x: (point.x + 0.15).toString(),
1114
- y: (point.y - 0.15).toString(),
1098
+ x: labelX.toString(),
1099
+ y: labelY.toString(),
1115
1100
  fill: colorMap.schematic.grid,
1116
- "font-size": "0.15",
1117
- "fill-opacity": "0.7"
1101
+ "font-size": (0.15 * Math.abs(transform.a)).toString(),
1102
+ "fill-opacity": "0.7",
1103
+ "text-anchor": "start",
1104
+ "dominant-baseline": "middle"
1118
1105
  },
1119
1106
  children: [
1120
1107
  {
@@ -1136,25 +1123,71 @@ function drawSchematicLabeledPoints(params) {
1136
1123
  };
1137
1124
  }
1138
1125
 
1139
- // lib/sch/convert-circuit-json-to-schematic-svg.ts
1140
- import { stringify as stringify2 } from "svgson";
1126
+ // lib/sch/get-schematic-bounds-from-circuit-json.ts
1127
+ function getSchematicBoundsFromCircuitJson(soup, padding = 0.5) {
1128
+ let minX = Number.POSITIVE_INFINITY;
1129
+ let minY = Number.POSITIVE_INFINITY;
1130
+ let maxX = Number.NEGATIVE_INFINITY;
1131
+ let maxY = Number.NEGATIVE_INFINITY;
1132
+ const portSize = 0.2;
1133
+ for (const item of soup) {
1134
+ if (item.type === "schematic_component") {
1135
+ updateBounds(item.center, item.size, item.rotation || 0);
1136
+ } else if (item.type === "schematic_port") {
1137
+ updateBounds(item.center, { width: portSize, height: portSize }, 0);
1138
+ } else if (item.type === "schematic_debug_object") {
1139
+ if (item.shape === "rect") {
1140
+ updateBounds(item.center, item.size, 0);
1141
+ } else if (item.shape === "line") {
1142
+ updateBounds(item.start, { width: 0.1, height: 0.1 }, 0);
1143
+ updateBounds(item.end, { width: 0.1, height: 0.1 }, 0);
1144
+ }
1145
+ }
1146
+ }
1147
+ minX -= padding;
1148
+ minY -= padding;
1149
+ maxX += padding;
1150
+ maxY += padding;
1151
+ return { minX, minY, maxX, maxY };
1152
+ function updateBounds(center, size, rotation) {
1153
+ const corners = [
1154
+ { x: -size.width / 2, y: -size.height / 2 },
1155
+ { x: size.width / 2, y: -size.height / 2 },
1156
+ { x: size.width / 2, y: size.height / 2 },
1157
+ { x: -size.width / 2, y: size.height / 2 }
1158
+ ];
1159
+ for (const corner of corners) {
1160
+ const rotatedX = corner.x * Math.cos(rotation) - corner.y * Math.sin(rotation) + center.x;
1161
+ const rotatedY = corner.x * Math.sin(rotation) + corner.y * Math.cos(rotation) + center.y;
1162
+ minX = Math.min(minX, rotatedX);
1163
+ minY = Math.min(minY, rotatedY);
1164
+ maxX = Math.max(maxX, rotatedX);
1165
+ maxY = Math.max(maxY, rotatedY);
1166
+ }
1167
+ }
1168
+ }
1141
1169
 
1142
1170
  // lib/sch/svg-object-fns/create-svg-objects-from-sch-component.ts
1171
+ import { su } from "@tscircuit/soup-util";
1143
1172
  import { getSvg, symbols } from "schematic-symbols";
1144
1173
  import { parseSync } from "svgson";
1174
+ import { applyToPoint as applyToPoint14 } from "transformation-matrix";
1145
1175
  function createSchematicComponent({
1146
1176
  component,
1177
+ transform,
1147
1178
  circuitJson
1148
1179
  }) {
1149
- const center = component.center;
1150
- const size = component.size;
1151
- const rotation = component.rotation;
1152
- const symbolName = component.symbol_name;
1180
+ const { center, size, rotation, symbol_name: symbolName } = component;
1153
1181
  const portLabels = component.port_labels;
1154
1182
  const sourceComponentId = component.source_component_id;
1155
1183
  const schematicComponentId = component.schematic_component_id;
1156
- const transformString = `translate(${center.x}, ${center.y}) rotate(${rotation * 180 / Math.PI})`;
1157
- let children = [];
1184
+ const [transformedX, transformedY] = applyToPoint14(transform, [
1185
+ center.x,
1186
+ center.y
1187
+ ]);
1188
+ const componentScale = Math.abs(transform.a);
1189
+ let componentChildren = [];
1190
+ const textChildren = [];
1158
1191
  const sourceComponent = circuitJson?.find(
1159
1192
  (item) => item.type === "source_component" && item.source_component_id === sourceComponentId
1160
1193
  );
@@ -1175,24 +1208,74 @@ function createSchematicComponent({
1175
1208
  height: size.height
1176
1209
  })
1177
1210
  );
1178
- children = svg.children.filter(
1211
+ componentChildren = svg.children.filter(
1179
1212
  (child) => child.name === "path" && child.attributes.fill !== "green"
1180
1213
  ).map((path) => {
1181
1214
  const currentStrokeWidth = Number.parseFloat(
1182
1215
  path.attributes["stroke-width"] || "0.02"
1183
1216
  );
1184
- const newStrokeWidth = (currentStrokeWidth * 1.5).toString();
1185
1217
  return {
1186
1218
  ...path,
1187
1219
  attributes: {
1188
1220
  ...path.attributes,
1189
1221
  stroke: path.attributes.stroke === "black" ? `${colorMap.schematic.component_outline}` : path.attributes.stroke,
1190
- "stroke-width": newStrokeWidth
1222
+ "stroke-width": currentStrokeWidth.toString()
1191
1223
  }
1192
1224
  };
1193
1225
  });
1226
+ if (resistance || capacitance) {
1227
+ const [textX, textY] = applyToPoint14(transform, [
1228
+ center.x,
1229
+ -(center.y - size.height / 2 - 0.2)
1230
+ ]);
1231
+ const labelOffset = componentScale * 0.4;
1232
+ textChildren.push({
1233
+ name: "text",
1234
+ type: "element",
1235
+ attributes: {
1236
+ class: "component-name",
1237
+ x: textX.toString(),
1238
+ y: textY.toString(),
1239
+ "text-anchor": "middle",
1240
+ "dominant-baseline": "auto",
1241
+ "font-size": "0.2"
1242
+ },
1243
+ children: [
1244
+ {
1245
+ type: "text",
1246
+ value: (resistance || capacitance || "").toString(),
1247
+ name: "",
1248
+ attributes: {},
1249
+ children: []
1250
+ }
1251
+ ],
1252
+ value: ""
1253
+ });
1254
+ textChildren.push({
1255
+ name: "text",
1256
+ type: "element",
1257
+ attributes: {
1258
+ class: "component-name",
1259
+ x: textX.toString(),
1260
+ y: (textY - labelOffset).toString(),
1261
+ "text-anchor": "middle",
1262
+ "dominant-baseline": "auto",
1263
+ "font-size": "0.2"
1264
+ },
1265
+ children: [
1266
+ {
1267
+ type: "text",
1268
+ value: componentName || "",
1269
+ name: "",
1270
+ attributes: {},
1271
+ children: []
1272
+ }
1273
+ ],
1274
+ value: ""
1275
+ });
1276
+ }
1194
1277
  } else {
1195
- children.push({
1278
+ componentChildren.push({
1196
1279
  name: "rect",
1197
1280
  type: "element",
1198
1281
  value: "",
@@ -1201,20 +1284,30 @@ function createSchematicComponent({
1201
1284
  x: (-size.width / 2).toString(),
1202
1285
  y: (-size.height / 2).toString(),
1203
1286
  width: size.width.toString(),
1204
- height: size.height.toString()
1287
+ height: size.height.toString(),
1288
+ "stroke-width": "0.02"
1205
1289
  },
1206
1290
  children: []
1207
1291
  });
1208
1292
  if (manufacturerNumber) {
1209
- children.push({
1293
+ const [textX, textY] = applyToPoint14(transform, [
1294
+ center.x,
1295
+ // Center X position
1296
+ -(center.y - size.height / 2 - 0.5)
1297
+ // Above the component top edge
1298
+ ]);
1299
+ const labelOffset = componentScale * 0.4;
1300
+ textChildren.push({
1210
1301
  name: "text",
1211
1302
  type: "element",
1212
1303
  attributes: {
1213
1304
  class: "component-name",
1214
- x: 1.2.toString(),
1215
- y: (-size.height / 2 - 0.5).toString(),
1305
+ x: textX.toString(),
1306
+ y: textY.toString(),
1216
1307
  "text-anchor": "right",
1217
- "dominant-baseline": "auto"
1308
+ // Center align text
1309
+ "dominant-baseline": "auto",
1310
+ "font-size": "0.2"
1218
1311
  },
1219
1312
  children: [
1220
1313
  {
@@ -1227,15 +1320,17 @@ function createSchematicComponent({
1227
1320
  ],
1228
1321
  value: ""
1229
1322
  });
1230
- children.push({
1323
+ textChildren.push({
1231
1324
  name: "text",
1232
1325
  type: "element",
1233
1326
  attributes: {
1234
1327
  class: "component-name",
1235
- x: 1.2.toString(),
1236
- y: (-size.height / 2 - 0.7).toString(),
1328
+ x: textX.toString(),
1329
+ y: (textY - labelOffset).toString(),
1237
1330
  "text-anchor": "right",
1238
- "dominant-baseline": "auto"
1331
+ // Center align text
1332
+ "dominant-baseline": "auto",
1333
+ "font-size": "0.2"
1239
1334
  },
1240
1335
  children: [
1241
1336
  {
@@ -1249,71 +1344,93 @@ function createSchematicComponent({
1249
1344
  value: ""
1250
1345
  });
1251
1346
  }
1252
- const schematicPorts = circuitJson?.filter(
1253
- (item) => item.type === "schematic_port" && item.schematic_component_id === schematicComponentId
1254
- ) || [];
1347
+ const schematicPorts = su(circuitJson).schematic_port.list({
1348
+ schematic_component_id: schematicComponentId
1349
+ });
1255
1350
  const portLength = 0.2;
1256
1351
  const circleRadius = 0.05;
1257
- for (const port of schematicPorts) {
1258
- const { x: portX, y: portY, pinNumber } = port.center;
1259
- const x = portX - center.x;
1260
- const y = portY - center.y;
1261
- let endX = x;
1262
- let endY = y;
1263
- let labelX = x;
1264
- let labelY = y;
1265
- let textAnchor = "middle";
1266
- let dominantBaseline = "middle";
1267
- switch (port.center.side) {
1352
+ for (const schPort of schematicPorts) {
1353
+ const { x: portX, y: portY } = schPort.center;
1354
+ const srcPort = su(circuitJson).source_port.get(
1355
+ schPort.source_port_id
1356
+ );
1357
+ const pinNumber = srcPort?.pin_number;
1358
+ const relX = portX - center.x;
1359
+ const relY = portY - center.y;
1360
+ let endX = relX;
1361
+ let endY = relY;
1362
+ const portSide = schPort.side ?? schPort.center.side;
1363
+ switch (portSide) {
1268
1364
  case "left":
1269
- endX = x - portLength;
1270
- labelX = x + 0.2;
1271
- textAnchor = "start";
1365
+ endX = relX - portLength;
1272
1366
  break;
1273
1367
  case "right":
1274
- endX = x + portLength;
1275
- labelX = x - 0.2;
1276
- textAnchor = "end";
1368
+ endX = relX + portLength;
1277
1369
  break;
1278
1370
  case "top":
1279
- endY = y - portLength;
1280
- labelY = y - 0.2;
1281
- dominantBaseline = "hanging";
1371
+ endY = relY - portLength;
1282
1372
  break;
1283
1373
  case "bottom":
1284
- endY = y + portLength;
1285
- labelY = y + 0.2;
1286
- dominantBaseline = "auto";
1374
+ endY = relY + portLength;
1287
1375
  break;
1288
1376
  }
1289
- children.push({
1377
+ componentChildren.push({
1290
1378
  name: "line",
1291
1379
  type: "element",
1292
1380
  attributes: {
1293
1381
  class: "component-pin",
1294
- x1: x.toString(),
1295
- y1: y.toString(),
1382
+ x1: relX.toString(),
1383
+ y1: relY.toString(),
1296
1384
  x2: endX.toString(),
1297
- y2: endY.toString()
1385
+ y2: endY.toString(),
1386
+ "stroke-width": "0.02"
1298
1387
  },
1299
1388
  value: "",
1300
1389
  children: []
1301
1390
  });
1302
- children.push({
1391
+ componentChildren.push({
1303
1392
  name: "circle",
1304
1393
  type: "element",
1305
1394
  attributes: {
1306
1395
  class: "component-pin",
1307
1396
  cx: endX.toString(),
1308
1397
  cy: endY.toString(),
1309
- r: circleRadius.toString()
1398
+ r: circleRadius.toString(),
1399
+ "stroke-width": "0.02"
1310
1400
  },
1311
1401
  value: "",
1312
1402
  children: []
1313
1403
  });
1404
+ const [portEndX, portEndY] = applyToPoint14(transform, [
1405
+ center.x + endX,
1406
+ center.y + endY
1407
+ ]);
1314
1408
  const labelKey = `pin${pinNumber}`;
1315
1409
  if (portLabels && labelKey in portLabels) {
1316
- children.push({
1410
+ const labelText = portLabels[labelKey];
1411
+ let labelX = portEndX;
1412
+ let labelY = portEndY;
1413
+ let textAnchor = "middle";
1414
+ const labelOffset = 0.6 * componentScale;
1415
+ switch (portSide) {
1416
+ case "left":
1417
+ labelX += labelOffset;
1418
+ labelY += 0;
1419
+ textAnchor = "start";
1420
+ break;
1421
+ case "right":
1422
+ labelX -= labelOffset;
1423
+ labelY += 0;
1424
+ textAnchor = "end";
1425
+ break;
1426
+ case "top":
1427
+ labelY -= labelOffset;
1428
+ break;
1429
+ case "bottom":
1430
+ labelY += labelOffset;
1431
+ break;
1432
+ }
1433
+ textChildren.push({
1317
1434
  name: "text",
1318
1435
  type: "element",
1319
1436
  attributes: {
@@ -1321,13 +1438,13 @@ function createSchematicComponent({
1321
1438
  x: labelX.toString(),
1322
1439
  y: labelY.toString(),
1323
1440
  "text-anchor": textAnchor,
1324
- "dominant-baseline": dominantBaseline,
1325
- "font-size": "0.2"
1441
+ "dominant-baseline": "middle",
1442
+ "font-size": (0.2 * componentScale).toString()
1326
1443
  },
1327
1444
  children: [
1328
1445
  {
1329
1446
  type: "text",
1330
- value: portLabels[labelKey],
1447
+ value: labelText,
1331
1448
  name: "",
1332
1449
  attributes: {},
1333
1450
  children: []
@@ -1336,120 +1453,111 @@ function createSchematicComponent({
1336
1453
  value: ""
1337
1454
  });
1338
1455
  }
1339
- const pinNumberX = endX;
1340
- let pinNumberY = endY;
1341
- const pinNumberAnchor = "middle";
1342
- let pinNumberBaseline = "middle";
1343
- switch (port.center.side) {
1344
- case "left":
1345
- case "right":
1346
- pinNumberY -= 0.15;
1347
- break;
1456
+ const pinNumberOffset = 0.2;
1457
+ let pinX = endX;
1458
+ let pinY = endY;
1459
+ let dominantBaseline = "auto";
1460
+ switch (portSide) {
1348
1461
  case "top":
1349
- pinNumberY -= 0.15;
1350
- pinNumberBaseline = "auto";
1462
+ pinY = -(portY - portLength + pinNumberOffset);
1463
+ pinX = portX;
1464
+ dominantBaseline = "auto";
1351
1465
  break;
1352
1466
  case "bottom":
1353
- pinNumberY += 0.15;
1354
- pinNumberBaseline = "hanging";
1467
+ pinY = portY + portLength - pinNumberOffset;
1468
+ pinX = portX;
1469
+ dominantBaseline = "hanging";
1470
+ break;
1471
+ case "left":
1472
+ pinX = portX - pinNumberOffset;
1473
+ pinY = portY + pinNumberOffset;
1474
+ dominantBaseline = "auto";
1475
+ break;
1476
+ case "right":
1477
+ pinX = portX + pinNumberOffset;
1478
+ pinY = portY + pinNumberOffset;
1479
+ dominantBaseline = "auto";
1355
1480
  break;
1356
1481
  }
1357
- children.push({
1482
+ const [transformedPinX, transformedPinY] = applyToPoint14(transform, [
1483
+ pinX,
1484
+ pinY
1485
+ ]);
1486
+ textChildren.push({
1358
1487
  name: "text",
1359
1488
  type: "element",
1360
1489
  attributes: {
1361
1490
  class: "pin-number",
1362
- x: pinNumberX.toString(),
1363
- y: pinNumberY.toString(),
1364
- "text-anchor": pinNumberAnchor,
1365
- "dominant-baseline": pinNumberBaseline,
1366
- "font-size": "0.15"
1491
+ x: transformedPinX.toString(),
1492
+ y: transformedPinY.toString(),
1493
+ "text-anchor": "middle",
1494
+ "dominant-baseline": dominantBaseline,
1495
+ "font-size": (0.15 * componentScale).toString()
1367
1496
  },
1368
- value: "",
1369
1497
  children: [
1370
1498
  {
1371
1499
  type: "text",
1372
- value: pinNumber.toString(),
1500
+ value: pinNumber?.toString() || "",
1373
1501
  name: "",
1374
1502
  attributes: {},
1375
1503
  children: []
1376
1504
  }
1377
- ]
1505
+ ],
1506
+ value: ""
1378
1507
  });
1379
1508
  }
1380
1509
  }
1381
- if (resistance || capacitance) {
1382
- children.push({
1383
- name: "text",
1384
- type: "element",
1385
- attributes: {
1386
- class: "component-name",
1387
- x: "0",
1388
- y: (-size.height / 2 - 0.2).toString(),
1389
- "text-anchor": "middle",
1390
- "dominant-baseline": "auto"
1391
- },
1392
- value: "",
1393
- children: [
1394
- {
1395
- type: "text",
1396
- value: (resistance || capacitance || "").toString(),
1397
- name: "",
1398
- attributes: {},
1399
- children: []
1400
- }
1401
- ]
1402
- });
1403
- children.push({
1404
- name: "text",
1405
- type: "element",
1406
- attributes: {
1407
- class: "component-name",
1408
- x: "0",
1409
- y: (-size.height / 2 - 0.5).toString(),
1410
- "text-anchor": "middle",
1411
- "dominant-baseline": "auto"
1412
- },
1413
- value: "",
1414
- children: [
1415
- {
1416
- type: "text",
1417
- value: componentName || "",
1418
- name: "",
1419
- attributes: {},
1420
- children: []
1421
- }
1422
- ]
1423
- });
1424
- }
1425
- return [
1426
- {
1427
- name: "g",
1428
- value: "",
1429
- type: "element",
1430
- attributes: { transform: transformString },
1431
- children
1432
- }
1433
- ];
1510
+ const componentGroup = {
1511
+ name: "g",
1512
+ value: "",
1513
+ type: "element",
1514
+ attributes: {
1515
+ transform: `translate(${transformedX}, ${transformedY}) rotate(${rotation * 180 / Math.PI}) scale(${componentScale})`
1516
+ },
1517
+ children: componentChildren
1518
+ };
1519
+ const textGroup = {
1520
+ name: "g",
1521
+ value: "",
1522
+ type: "element",
1523
+ attributes: {},
1524
+ children: textChildren
1525
+ };
1526
+ return [componentGroup, textGroup];
1434
1527
  }
1435
1528
 
1436
1529
  // lib/sch/svg-object-fns/create-svg-objects-from-sch-debug-object.ts
1437
- function createSvgObjectsFromSchDebugObject(debugObject) {
1530
+ import { applyToPoint as applyToPoint15 } from "transformation-matrix";
1531
+ function createSvgObjectsFromSchDebugObject(debugObject, transform) {
1438
1532
  if (debugObject.shape === "rect") {
1533
+ const x = debugObject.center.x - debugObject.size.width / 2;
1534
+ const y = -(debugObject.center.y - debugObject.size.height / 2);
1535
+ const [transformedX, transformedY] = applyToPoint15(transform, [x, y]);
1536
+ const [transformedRight, transformedBottom] = applyToPoint15(transform, [
1537
+ x + debugObject.size.width,
1538
+ y - debugObject.size.height
1539
+ // Flip height direction
1540
+ ]);
1541
+ const width = Math.abs(transformedRight - transformedX);
1542
+ const height = Math.abs(transformedBottom - transformedY);
1543
+ const [centerX, centerY] = applyToPoint15(transform, [
1544
+ debugObject.center.x,
1545
+ -debugObject.center.y
1546
+ ]);
1439
1547
  return [
1440
1548
  {
1441
1549
  name: "rect",
1442
1550
  type: "element",
1443
1551
  value: "",
1444
1552
  attributes: {
1445
- x: (debugObject.center.x - debugObject.size.width / 2).toString(),
1446
- y: (debugObject.center.y - debugObject.size.height / 2).toString(),
1447
- width: debugObject.size.width.toString(),
1448
- height: debugObject.size.height.toString(),
1553
+ x: transformedX.toString(),
1554
+ y: Math.min(transformedY, transformedBottom).toString(),
1555
+ width: width.toString(),
1556
+ height: height.toString(),
1449
1557
  fill: "none",
1450
1558
  stroke: "red",
1451
- "stroke-width": "0.02",
1452
- "stroke-dasharray": "0.1,0.1"
1559
+ "stroke-width": (0.02 * Math.abs(transform.a)).toString(),
1560
+ "stroke-dasharray": "5,5"
1453
1561
  },
1454
1562
  children: debugObject.label ? [
1455
1563
  {
@@ -1457,10 +1565,10 @@ function createSvgObjectsFromSchDebugObject(debugObject) {
1457
1565
  type: "element",
1458
1566
  value: "",
1459
1567
  attributes: {
1460
- x: debugObject.center.x.toString(),
1461
- y: (debugObject.center.y - debugObject.size.height / 2 - 0.1).toString(),
1568
+ x: centerX.toString(),
1569
+ y: (centerY - 10).toString(),
1462
1570
  "text-anchor": "middle",
1463
- "font-size": "0.2",
1571
+ "font-size": (0.2 * Math.abs(transform.a)).toString(),
1464
1572
  fill: "red"
1465
1573
  },
1466
1574
  children: [
@@ -1478,19 +1586,31 @@ function createSvgObjectsFromSchDebugObject(debugObject) {
1478
1586
  ];
1479
1587
  }
1480
1588
  if (debugObject.shape === "line") {
1589
+ const [startX, startY] = applyToPoint15(transform, [
1590
+ debugObject.start.x,
1591
+ -debugObject.start.y
1592
+ // Flip Y
1593
+ ]);
1594
+ const [endX, endY] = applyToPoint15(transform, [
1595
+ debugObject.end.x,
1596
+ -debugObject.end.y
1597
+ // Flip Y
1598
+ ]);
1599
+ const midX = (startX + endX) / 2;
1600
+ const midY = (startY + endY) / 2;
1481
1601
  return [
1482
1602
  {
1483
1603
  name: "line",
1484
1604
  type: "element",
1485
1605
  value: "",
1486
1606
  attributes: {
1487
- x1: debugObject.start.x.toString(),
1488
- y1: debugObject.start.y.toString(),
1489
- x2: debugObject.end.x.toString(),
1490
- y2: debugObject.end.y.toString(),
1607
+ x1: startX.toString(),
1608
+ y1: startY.toString(),
1609
+ x2: endX.toString(),
1610
+ y2: endY.toString(),
1491
1611
  stroke: "red",
1492
- "stroke-width": "0.02",
1493
- "stroke-dasharray": "0.1,0.1"
1612
+ "stroke-width": (0.02 * Math.abs(transform.a)).toString(),
1613
+ "stroke-dasharray": "5,5"
1494
1614
  },
1495
1615
  children: debugObject.label ? [
1496
1616
  {
@@ -1498,10 +1618,10 @@ function createSvgObjectsFromSchDebugObject(debugObject) {
1498
1618
  type: "element",
1499
1619
  value: "",
1500
1620
  attributes: {
1501
- x: ((debugObject.start.x + debugObject.end.x) / 2).toString(),
1502
- y: ((debugObject.start.y + debugObject.end.y) / 2 - 0.1).toString(),
1621
+ x: midX.toString(),
1622
+ y: (midY - 10).toString(),
1503
1623
  "text-anchor": "middle",
1504
- "font-size": "0.2",
1624
+ "font-size": (0.2 * Math.abs(transform.a)).toString(),
1505
1625
  fill: "red"
1506
1626
  },
1507
1627
  children: [
@@ -1522,22 +1642,32 @@ function createSvgObjectsFromSchDebugObject(debugObject) {
1522
1642
  }
1523
1643
 
1524
1644
  // lib/sch/svg-object-fns/create-svg-objects-from-sch-trace.ts
1525
- function createSchematicTrace(trace, flipY, portPositions) {
1645
+ import { applyToPoint as applyToPoint16 } from "transformation-matrix";
1646
+ function createSchematicTrace(trace, transform) {
1526
1647
  const edges = trace.edges;
1527
1648
  if (edges.length === 0) return [];
1528
1649
  let path = "";
1529
1650
  edges.forEach((edge, index) => {
1530
- const fromPoint = edge.from.ti !== void 0 ? portPositions.get(edge.from.ti) : edge.from;
1531
- const toPoint = edge.to.ti !== void 0 ? portPositions.get(edge.to.ti) : edge.to;
1532
- if (!fromPoint || !toPoint) {
1533
- return;
1534
- }
1535
- const fromCoord = `${fromPoint.x - 0.15} ${flipY(fromPoint.y)}`;
1536
- const toCoord = `${toPoint.x + 0.15} ${flipY(toPoint.y)}`;
1651
+ const fromPoint = {
1652
+ x: (edge.from.x ?? edge.from.center?.x) - 0.15,
1653
+ y: edge.from.y ?? edge.from.center?.y
1654
+ };
1655
+ const toPoint = {
1656
+ x: (edge.to.x ?? edge.to.center?.x) + 0.15,
1657
+ y: edge.to.y ?? edge.to.center?.y
1658
+ };
1659
+ const [transformedFromX, transformedFromY] = applyToPoint16(transform, [
1660
+ fromPoint.x,
1661
+ fromPoint.y
1662
+ ]);
1663
+ const [transformedToX, transformedToY] = applyToPoint16(transform, [
1664
+ toPoint.x,
1665
+ toPoint.y
1666
+ ]);
1537
1667
  if (index === 0) {
1538
- path += `M ${fromCoord} L ${toCoord}`;
1668
+ path += `M ${transformedFromX} ${transformedFromY} L ${transformedToX} ${transformedToY}`;
1539
1669
  } else {
1540
- path += ` L ${toCoord}`;
1670
+ path += ` L ${transformedToX} ${transformedToY}`;
1541
1671
  }
1542
1672
  });
1543
1673
  return path ? [
@@ -1546,7 +1676,9 @@ function createSchematicTrace(trace, flipY, portPositions) {
1546
1676
  type: "element",
1547
1677
  attributes: {
1548
1678
  class: "trace",
1549
- d: path
1679
+ d: path,
1680
+ "stroke-width": (0.02 * Math.abs(transform.a)).toString()
1681
+ // Scale stroke width with transform
1550
1682
  },
1551
1683
  value: "",
1552
1684
  children: []
@@ -1555,23 +1687,41 @@ function createSchematicTrace(trace, flipY, portPositions) {
1555
1687
  }
1556
1688
 
1557
1689
  // lib/sch/convert-circuit-json-to-schematic-svg.ts
1558
- import { identity } from "transformation-matrix";
1559
1690
  function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1560
- const portPositions = /* @__PURE__ */ new Map();
1561
- for (const item of circuitJson) {
1562
- if (item.type === "schematic_port") {
1563
- portPositions.set(item.schematic_port_id, item.center);
1564
- }
1565
- }
1566
1691
  const bounds = getSchematicBoundsFromCircuitJson(circuitJson);
1567
1692
  const { minX, minY, maxX, maxY } = bounds;
1568
- const viewBoxPadding = 0.5;
1569
- const width = maxX - minX + 2 * viewBoxPadding;
1570
- const height = maxY - minY;
1571
- const viewBox = `${minX - viewBoxPadding} ${minY - viewBoxPadding} ${width} ${height + 2 * viewBoxPadding}`;
1572
- const flipY = (y) => height - (y - minY) + minY;
1573
- const transform = identity();
1693
+ const padding = 1;
1694
+ const circuitWidth = maxX - minX + 2 * padding;
1695
+ const circuitHeight = maxY - minY + 2 * padding;
1696
+ const svgWidth = options?.width ?? 1200;
1697
+ const svgHeight = options?.height ?? 600;
1698
+ const scaleX = svgWidth / circuitWidth;
1699
+ const scaleY = svgHeight / circuitHeight;
1700
+ const scaleFactor = Math.min(scaleX, scaleY);
1701
+ const offsetX = (svgWidth - circuitWidth * scaleFactor) / 2;
1702
+ const offsetY = (svgHeight - circuitHeight * scaleFactor) / 2;
1703
+ const transform = compose4(
1704
+ translate4(
1705
+ offsetX - minX * scaleFactor + padding * scaleFactor,
1706
+ svgHeight - offsetY + minY * scaleFactor - padding * scaleFactor
1707
+ ),
1708
+ scale2(scaleFactor, -scaleFactor)
1709
+ // Flip in y-direction
1710
+ );
1574
1711
  const svgChildren = [];
1712
+ svgChildren.push({
1713
+ name: "rect",
1714
+ type: "element",
1715
+ attributes: {
1716
+ class: "boundary",
1717
+ x: "0",
1718
+ y: "0",
1719
+ width: svgWidth.toString(),
1720
+ height: svgHeight.toString()
1721
+ },
1722
+ children: [],
1723
+ value: ""
1724
+ });
1575
1725
  if (options?.grid) {
1576
1726
  const gridConfig = typeof options.grid === "object" ? options.grid : {};
1577
1727
  svgChildren.push(drawSchematicGrid({ bounds, transform, ...gridConfig }));
@@ -1589,21 +1739,19 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1589
1739
  const schTraceSvgs = [];
1590
1740
  for (const elm of circuitJson) {
1591
1741
  if (elm.type === "schematic_debug_object") {
1592
- schDebugObjectSvgs.push(...createSvgObjectsFromSchDebugObject(elm));
1742
+ schDebugObjectSvgs.push(
1743
+ ...createSvgObjectsFromSchDebugObject(elm, transform)
1744
+ );
1593
1745
  } else if (elm.type === "schematic_component") {
1594
1746
  schComponentSvgs.push(
1595
1747
  ...createSchematicComponent({
1596
- component: {
1597
- ...elm,
1598
- center: { x: elm.center.x, y: flipY(elm.center.y) }
1599
- },
1748
+ component: elm,
1600
1749
  transform,
1601
- // Add the missing transform property
1602
1750
  circuitJson
1603
1751
  })
1604
1752
  );
1605
1753
  } else if (elm.type === "schematic_trace") {
1606
- schTraceSvgs.push(...createSchematicTrace(elm, flipY, portPositions));
1754
+ schTraceSvgs.push(...createSchematicTrace(elm, transform));
1607
1755
  }
1608
1756
  }
1609
1757
  svgChildren.push(...schDebugObjectSvgs, ...schComponentSvgs, ...schTraceSvgs);
@@ -1612,12 +1760,12 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1612
1760
  type: "element",
1613
1761
  attributes: {
1614
1762
  xmlns: "http://www.w3.org/2000/svg",
1615
- viewBox,
1616
- width: (options?.width ?? 1200).toString(),
1617
- height: (options?.height ?? 600).toString(),
1763
+ width: svgWidth.toString(),
1764
+ height: svgHeight.toString(),
1618
1765
  style: `background-color: ${colorMap.schematic.background}`
1619
1766
  },
1620
1767
  children: [
1768
+ // Add styles
1621
1769
  {
1622
1770
  name: "style",
1623
1771
  type: "element",
@@ -1625,14 +1773,16 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1625
1773
  {
1626
1774
  type: "text",
1627
1775
  value: `
1628
- .component { fill: none; stroke: ${colorMap.schematic.component_outline}; stroke-width: 0.03; }
1629
- .chip { fill: ${colorMap.schematic.component_body}; stroke: ${colorMap.schematic.component_outline}; stroke-width: 0.03; }
1630
- .component-pin { fill: none; stroke: ${colorMap.schematic.component_outline}; stroke-width: 0.02; }
1631
- .trace { stroke: ${colorMap.schematic.wire}; stroke-width: 0.02; fill: none; }
1632
- .text { font-family: Arial, sans-serif; font-size: 0.2px; fill: ${colorMap.schematic.wire}; }
1633
- .pin-number { font-size: 0.15px; fill: ${colorMap.schematic.pin_number}; }
1776
+ .boundary { fill: ${colorMap.schematic.background}; }
1777
+ .schematic-boundary { fill: none; stroke: #fff; stroke-width: 0.3; }
1778
+ .component { fill: none; stroke: ${colorMap.schematic.component_outline}; }
1779
+ .chip { fill: ${colorMap.schematic.component_body}; stroke: ${colorMap.schematic.component_outline}; }
1780
+ .component-pin { fill: none; stroke: ${colorMap.schematic.component_outline}; }
1781
+ .trace { stroke: ${colorMap.schematic.wire}; stroke-width: ${0.02 * scaleFactor}; fill: none; }
1782
+ .text { font-family: Arial, sans-serif; font-size: ${0.2 * scaleFactor}px; fill: ${colorMap.schematic.wire}; }
1783
+ .pin-number { font-size: ${0.15 * scaleFactor}px; fill: ${colorMap.schematic.pin_number}; }
1634
1784
  .port-label { fill: ${colorMap.schematic.reference}; }
1635
- .component-name { font-size: 0.25px; fill: ${colorMap.schematic.reference}; }
1785
+ .component-name { font-size: ${0.25 * scaleFactor}px; fill: ${colorMap.schematic.reference}; }
1636
1786
  `,
1637
1787
  name: "",
1638
1788
  attributes: {},
@@ -1646,14 +1796,7 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1646
1796
  ],
1647
1797
  value: ""
1648
1798
  };
1649
- return stringify2({
1650
- ...svgObject,
1651
- attributes: {
1652
- ...svgObject.attributes,
1653
- width: svgObject.attributes.width?.toString(),
1654
- height: svgObject.attributes.height?.toString()
1655
- }
1656
- });
1799
+ return stringify2(svgObject);
1657
1800
  }
1658
1801
  var circuitJsonToSchematicSvg = convertCircuitJsonToSchematicSvg;
1659
1802
  export {