circuit-to-svg 0.0.56 → 0.0.58

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
@@ -979,9 +979,7 @@ var colorMap = {
979
979
  // lib/sch/convert-circuit-json-to-schematic-svg.ts
980
980
  import { stringify as stringify2 } from "svgson";
981
981
  import {
982
- compose as compose4,
983
- scale as scale2,
984
- translate as translate4
982
+ fromTriangles
985
983
  } from "transformation-matrix";
986
984
 
987
985
  // lib/sch/draw-schematic-grid.ts
@@ -1038,12 +1036,13 @@ function drawSchematicGrid(params) {
1038
1036
  type: "element",
1039
1037
  attributes: {
1040
1038
  x: (point.x - 2.5).toString(),
1041
- y: (point.y - 2.5).toString(),
1039
+ y: (point.y - 5).toString(),
1042
1040
  fill: colorMap.schematic.grid,
1043
1041
  "font-size": (cellSize / 5 * Math.abs(params.transform.a)).toString(),
1044
1042
  "fill-opacity": "0.5",
1045
1043
  "text-anchor": "middle",
1046
- "dominant-baseline": "middle"
1044
+ "dominant-baseline": "middle",
1045
+ "font-family": "sans-serif"
1047
1046
  },
1048
1047
  children: [
1049
1048
  {
@@ -1167,381 +1166,587 @@ function getSchematicBoundsFromCircuitJson(soup, padding = 0.5) {
1167
1166
  }
1168
1167
  }
1169
1168
 
1170
- // lib/sch/svg-object-fns/create-svg-objects-from-sch-component.ts
1169
+ // lib/sch/svg-object-fns/create-svg-objects-from-sch-component-with-symbol.ts
1171
1170
  import { su } from "@tscircuit/soup-util";
1172
- import { getSvg, symbols } from "schematic-symbols";
1173
- import { parseSync } from "svgson";
1174
- import { applyToPoint as applyToPoint14 } from "transformation-matrix";
1175
- function createSchematicComponent({
1176
- component,
1177
- transform,
1171
+ import { symbols } from "schematic-symbols";
1172
+ import "svgson";
1173
+ import {
1174
+ applyToPoint as applyToPoint15,
1175
+ compose as compose5
1176
+ } from "transformation-matrix";
1177
+
1178
+ // lib/utils/get-sch-stroke-size.ts
1179
+ var getSchStrokeSize = (transform) => {
1180
+ return Math.abs(transform.a) * 0.02;
1181
+ };
1182
+
1183
+ // lib/utils/match-sch-ports-with-symbol-ports.ts
1184
+ var getAngularDifference = (angle1, angle2) => {
1185
+ const a1 = angle1 < 0 ? angle1 + 2 * Math.PI : angle1;
1186
+ const a2 = angle2 < 0 ? angle2 + 2 * Math.PI : angle2;
1187
+ let diff = Math.abs(a1 - a2);
1188
+ if (diff > Math.PI) {
1189
+ diff = 2 * Math.PI - diff;
1190
+ }
1191
+ return diff;
1192
+ };
1193
+ var matchSchPortsToSymbolPorts = ({
1194
+ schPorts,
1195
+ symbol,
1196
+ schComponent
1197
+ }) => {
1198
+ const schPortAngles = schPorts.map((port) => {
1199
+ const dx = port.center.x - schComponent.center.x;
1200
+ const dy = port.center.y - schComponent.center.y;
1201
+ return {
1202
+ port,
1203
+ angle: Math.atan2(dy, dx)
1204
+ };
1205
+ });
1206
+ const symbolPortAngles = symbol.ports.map((port) => {
1207
+ const dx = port.x - symbol.center.x;
1208
+ const dy = port.y - symbol.center.y;
1209
+ return {
1210
+ port,
1211
+ angle: Math.atan2(dy, dx)
1212
+ };
1213
+ });
1214
+ schPortAngles.sort((a, b) => a.angle - b.angle);
1215
+ symbolPortAngles.sort((a, b) => a.angle - b.angle);
1216
+ const matches = [];
1217
+ const usedSymbolPorts = /* @__PURE__ */ new Set();
1218
+ for (const schPortAngle of schPortAngles) {
1219
+ let bestMatch = null;
1220
+ for (const symbolPortAngle of symbolPortAngles) {
1221
+ if (usedSymbolPorts.has(symbolPortAngle.port)) continue;
1222
+ const angleDiff = getAngularDifference(
1223
+ schPortAngle.angle,
1224
+ symbolPortAngle.angle
1225
+ );
1226
+ if (bestMatch === null || angleDiff < bestMatch.angleDiff) {
1227
+ bestMatch = {
1228
+ symbolPort: symbolPortAngle.port,
1229
+ angleDiff
1230
+ };
1231
+ }
1232
+ }
1233
+ if (bestMatch && bestMatch.angleDiff < Math.PI / 4) {
1234
+ matches.push({
1235
+ schPort: schPortAngle.port,
1236
+ symbolPort: bestMatch.symbolPort
1237
+ });
1238
+ usedSymbolPorts.add(bestMatch.symbolPort);
1239
+ }
1240
+ }
1241
+ return matches;
1242
+ };
1243
+
1244
+ // lib/utils/point-pairs-to-matrix.ts
1245
+ import { compose as compose4, scale as scale2, translate as translate4 } from "transformation-matrix";
1246
+ function pointPairsToMatrix(a1, a2, b1, b2) {
1247
+ const tx = a2.x - a1.x;
1248
+ const ty = a2.y - a1.y;
1249
+ const originalDistance = Math.sqrt((b1.x - a1.x) ** 2 + (b1.y - a1.y) ** 2);
1250
+ const transformedDistance = Math.sqrt((b2.x - a2.x) ** 2 + (b2.y - a2.y) ** 2);
1251
+ const a = transformedDistance / originalDistance;
1252
+ const translateMatrix = translate4(tx, ty);
1253
+ const scaleMatrix = scale2(a, a);
1254
+ return compose4(translateMatrix, scaleMatrix);
1255
+ }
1256
+
1257
+ // lib/utils/get-sch-font-size.ts
1258
+ import "transformation-matrix";
1259
+ var getSchFontSize = (transform, textType) => {
1260
+ return Math.abs(transform.a) * (textType === "pin_number" ? 0.15 : 0.18);
1261
+ };
1262
+
1263
+ // lib/sch/svg-object-fns/create-svg-objects-from-sch-component-with-symbol.ts
1264
+ var ninePointAnchorToTextAnchor = {
1265
+ top_left: "start",
1266
+ top_right: "end",
1267
+ middle_left: "start",
1268
+ middle_right: "end",
1269
+ bottom_left: "start",
1270
+ bottom_right: "end",
1271
+ center: "middle",
1272
+ middle_top: "middle",
1273
+ middle_bottom: "middle"
1274
+ };
1275
+ var ninePointAnchorToDominantBaseline = {
1276
+ top_left: "auto",
1277
+ top_right: "auto",
1278
+ bottom_left: "hanging",
1279
+ bottom_right: "hanging",
1280
+ center: "auto",
1281
+ middle_left: "middle",
1282
+ middle_right: "middle",
1283
+ middle_top: "auto",
1284
+ middle_bottom: "hanging"
1285
+ };
1286
+ var createSvgObjectsFromSchematicComponentWithSymbol = ({
1287
+ component: schComponent,
1288
+ transform: realToScreenTransform,
1178
1289
  circuitJson
1179
- }) {
1180
- const { center, size, rotation, symbol_name: symbolName } = component;
1181
- const portLabels = component.port_labels;
1182
- const sourceComponentId = component.source_component_id;
1183
- const schematicComponentId = component.schematic_component_id;
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 = [];
1191
- const sourceComponent = circuitJson?.find(
1192
- (item) => item.type === "source_component" && item.source_component_id === sourceComponentId
1290
+ }) => {
1291
+ const svgObjects = [];
1292
+ const symbol = symbols[schComponent.symbol_name];
1293
+ if (!symbol) return [];
1294
+ const schPorts = su(circuitJson).schematic_port.list({
1295
+ schematic_component_id: schComponent.schematic_component_id
1296
+ });
1297
+ const srcComponent = su(circuitJson).source_component.get(
1298
+ schComponent.source_component_id
1193
1299
  );
1194
- const componentName = sourceComponent && "name" in sourceComponent ? sourceComponent.name : "";
1195
- const manufacturerNumber = sourceComponent && "manufacturer_part_number" in sourceComponent ? sourceComponent.manufacturer_part_number : "";
1196
- const resistance = sourceComponent && "resistance" in sourceComponent ? sourceComponent.resistance : "";
1197
- const capacitance = sourceComponent && "capacitance" in sourceComponent ? sourceComponent.capacitance : "";
1198
- if (symbolName) {
1199
- const symbol = symbols[symbolName];
1200
- const paths = symbol.primitives.filter((p) => p.type === "path");
1201
- const updatedSymbol = {
1202
- ...symbol,
1203
- primitives: paths
1204
- };
1205
- const svg = parseSync(
1206
- getSvg(updatedSymbol, {
1207
- width: size.width,
1208
- height: size.height
1209
- })
1300
+ const schPortsWithSymbolPorts = matchSchPortsToSymbolPorts({
1301
+ schPorts,
1302
+ symbol,
1303
+ schComponent
1304
+ });
1305
+ if (!schPortsWithSymbolPorts[0]) return [];
1306
+ const transformFromSymbolToReal = pointPairsToMatrix(
1307
+ schPortsWithSymbolPorts[1]?.symbolPort ?? symbol.center,
1308
+ schPortsWithSymbolPorts[1]?.schPort.center ?? schComponent.center,
1309
+ schPortsWithSymbolPorts[0].symbolPort,
1310
+ schPortsWithSymbolPorts[0].schPort.center
1311
+ );
1312
+ const paths = symbol.primitives.filter((p) => p.type === "path");
1313
+ const texts = symbol.primitives.filter((p) => p.type === "text");
1314
+ const circles = symbol.primitives.filter((p) => p.type === "circle");
1315
+ const boxes = symbol.primitives.filter((p) => p.type === "box");
1316
+ for (const path of paths) {
1317
+ const { points, color, closed, fill } = path;
1318
+ svgObjects.push({
1319
+ type: "element",
1320
+ name: "path",
1321
+ attributes: {
1322
+ d: points.map((p, i) => {
1323
+ const [x, y] = applyToPoint15(
1324
+ compose5(realToScreenTransform, transformFromSymbolToReal),
1325
+ [p.x, p.y]
1326
+ );
1327
+ return `${i === 0 ? "M" : "L"} ${x} ${y}`;
1328
+ }).join(" ") + (closed ? " Z" : ""),
1329
+ stroke: colorMap.schematic.component_outline,
1330
+ fill: "none",
1331
+ "stroke-width": `${getSchStrokeSize(realToScreenTransform)}px`
1332
+ },
1333
+ value: "",
1334
+ children: []
1335
+ });
1336
+ }
1337
+ for (const text of texts) {
1338
+ const screenTextPos = applyToPoint15(
1339
+ compose5(realToScreenTransform, transformFromSymbolToReal),
1340
+ text
1210
1341
  );
1211
- componentChildren = svg.children.filter(
1212
- (child) => child.name === "path" && child.attributes.fill !== "green"
1213
- ).map((path) => {
1214
- const currentStrokeWidth = Number.parseFloat(
1215
- path.attributes["stroke-width"] || "0.02"
1216
- );
1217
- return {
1218
- ...path,
1219
- attributes: {
1220
- ...path.attributes,
1221
- stroke: path.attributes.stroke === "black" ? `${colorMap.schematic.component_outline}` : path.attributes.stroke,
1222
- "stroke-width": currentStrokeWidth.toString()
1342
+ let textValue = "";
1343
+ if (text.text === "{REF}") {
1344
+ textValue = srcComponent?.name ?? "";
1345
+ } else if (text.text === "{VAL}") {
1346
+ textValue = srcComponent?.display_value ?? srcComponent.capacitance ?? srcComponent.resistance;
1347
+ }
1348
+ svgObjects.push({
1349
+ name: "text",
1350
+ type: "element",
1351
+ attributes: {
1352
+ x: screenTextPos.x.toString(),
1353
+ y: screenTextPos.y.toString(),
1354
+ "dominant-baseline": ninePointAnchorToDominantBaseline[text.anchor],
1355
+ "text-anchor": ninePointAnchorToTextAnchor[text.anchor],
1356
+ "font-family": "sans-serif",
1357
+ "font-size": `${getSchFontSize(realToScreenTransform, "reference_designator")}px`
1358
+ },
1359
+ value: "",
1360
+ children: [
1361
+ {
1362
+ type: "text",
1363
+ value: textValue,
1364
+ name: "",
1365
+ attributes: {},
1366
+ children: []
1223
1367
  }
1224
- };
1368
+ ]
1225
1369
  });
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
- }
1277
- } else {
1278
- componentChildren.push({
1370
+ }
1371
+ for (const box of boxes) {
1372
+ const screenBoxPos = applyToPoint15(
1373
+ compose5(realToScreenTransform, transformFromSymbolToReal),
1374
+ box
1375
+ );
1376
+ const symbolToScreenScale = compose5(
1377
+ realToScreenTransform,
1378
+ transformFromSymbolToReal
1379
+ ).a;
1380
+ svgObjects.push({
1279
1381
  name: "rect",
1280
1382
  type: "element",
1281
- value: "",
1282
1383
  attributes: {
1283
- class: "component chip",
1284
- x: (-size.width / 2).toString(),
1285
- y: (-size.height / 2).toString(),
1286
- width: size.width.toString(),
1287
- height: size.height.toString(),
1288
- "stroke-width": "0.02"
1384
+ x: screenBoxPos.x.toString(),
1385
+ y: screenBoxPos.y.toString(),
1386
+ width: (box.width * symbolToScreenScale).toString(),
1387
+ height: (box.height * symbolToScreenScale).toString(),
1388
+ fill: "red"
1289
1389
  },
1390
+ value: "",
1290
1391
  children: []
1291
1392
  });
1292
- if (manufacturerNumber) {
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({
1301
- name: "text",
1302
- type: "element",
1303
- attributes: {
1304
- class: "component-name",
1305
- x: textX.toString(),
1306
- y: textY.toString(),
1307
- "text-anchor": "right",
1308
- // Center align text
1309
- "dominant-baseline": "auto",
1310
- "font-size": "0.2"
1311
- },
1312
- children: [
1313
- {
1314
- type: "text",
1315
- value: manufacturerNumber,
1316
- name: "",
1317
- attributes: {},
1318
- children: []
1319
- }
1320
- ],
1321
- value: ""
1322
- });
1323
- textChildren.push({
1324
- name: "text",
1325
- type: "element",
1326
- attributes: {
1327
- class: "component-name",
1328
- x: textX.toString(),
1329
- y: (textY - labelOffset).toString(),
1330
- "text-anchor": "right",
1331
- // Center align text
1332
- "dominant-baseline": "auto",
1333
- "font-size": "0.2"
1334
- },
1335
- children: [
1336
- {
1337
- type: "text",
1338
- value: componentName || "",
1339
- name: "",
1340
- attributes: {},
1341
- children: []
1342
- }
1343
- ],
1344
- value: ""
1345
- });
1346
- }
1347
- const schematicPorts = su(circuitJson).schematic_port.list({
1348
- schematic_component_id: schematicComponentId
1393
+ }
1394
+ for (const port of symbol.ports) {
1395
+ const screenPortPos = applyToPoint15(
1396
+ compose5(realToScreenTransform, transformFromSymbolToReal),
1397
+ port
1398
+ );
1399
+ svgObjects.push({
1400
+ type: "element",
1401
+ name: "circle",
1402
+ attributes: {
1403
+ cx: screenPortPos.x.toString(),
1404
+ cy: screenPortPos.y.toString(),
1405
+ r: `${realToScreenTransform.a * 0.02}px`,
1406
+ fill: "none",
1407
+ stroke: colorMap.schematic.component_outline
1408
+ },
1409
+ value: "",
1410
+ children: []
1349
1411
  });
1350
- const portLength = 0.2;
1351
- const circleRadius = 0.05;
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
- if (portSide === "center") {
1364
- continue;
1365
- }
1366
- switch (portSide) {
1367
- case "left":
1368
- endX = relX + portLength;
1369
- break;
1370
- case "right":
1371
- endX = relX - portLength;
1372
- break;
1373
- case "top":
1374
- endY = relY + portLength;
1375
- break;
1376
- case "bottom":
1377
- endY = relY - portLength;
1378
- break;
1379
- }
1380
- componentChildren.push({
1381
- name: "line",
1382
- type: "element",
1383
- attributes: {
1384
- class: "component-pin",
1385
- x1: relX.toString(),
1386
- y1: relY.toString(),
1387
- x2: endX.toString(),
1388
- y2: endY.toString(),
1389
- "stroke-width": "0.02"
1390
- },
1391
- value: "",
1392
- children: []
1393
- });
1394
- componentChildren.push({
1395
- name: "circle",
1396
- type: "element",
1397
- attributes: {
1398
- class: "component-pin",
1399
- cx: relX.toString(),
1400
- cy: relY.toString(),
1401
- r: circleRadius.toString(),
1402
- "stroke-width": "0.02"
1403
- },
1404
- value: "",
1405
- children: []
1406
- });
1407
- const [portEndX, portEndY] = applyToPoint14(transform, [
1408
- center.x + endX,
1409
- center.y + endY
1410
- ]);
1411
- const labelKey = `pin${pinNumber}`;
1412
- if (portLabels && labelKey in portLabels) {
1413
- const labelText = portLabels[labelKey];
1414
- let labelX = portEndX;
1415
- let labelY = portEndY;
1416
- let textAnchor = "middle";
1417
- let rotation2 = 0;
1418
- const labelOffset = 0.1 * componentScale;
1419
- switch (portSide) {
1420
- case "left":
1421
- labelX += labelOffset;
1422
- textAnchor = "start";
1423
- break;
1424
- case "right":
1425
- labelX -= labelOffset;
1426
- textAnchor = "end";
1427
- break;
1428
- case "top":
1429
- labelY += labelOffset * 6;
1430
- textAnchor = "start";
1431
- rotation2 = -90;
1432
- break;
1433
- case "bottom":
1434
- labelY -= labelOffset * 6;
1435
- textAnchor = "end";
1436
- rotation2 = -90;
1437
- break;
1438
- }
1439
- textChildren.push({
1440
- name: "text",
1441
- type: "element",
1442
- attributes: {
1443
- class: "port-label",
1444
- x: labelX.toString(),
1445
- y: labelY.toString(),
1446
- "text-anchor": textAnchor,
1447
- "dominant-baseline": "middle",
1448
- "font-size": (0.2 * componentScale).toString(),
1449
- transform: rotation2 ? `rotate(${rotation2}, ${labelX}, ${labelY})` : ""
1450
- },
1451
- children: [
1452
- {
1453
- type: "text",
1454
- value: labelText,
1455
- name: "",
1456
- attributes: {},
1457
- children: []
1458
- }
1459
- ],
1460
- value: ""
1461
- });
1462
- }
1463
- const pinNumberOffset = 0.2;
1464
- let pinX = endX;
1465
- let pinY = endY;
1466
- let dominantBaseline = "auto";
1467
- switch (portSide) {
1468
- case "top":
1469
- pinY = portY - pinNumberOffset;
1470
- pinX = portX;
1471
- dominantBaseline = "auto";
1472
- break;
1473
- case "bottom":
1474
- pinY = portY + pinNumberOffset;
1475
- pinX = portX;
1476
- dominantBaseline = "hanging";
1477
- break;
1478
- case "left":
1479
- pinX = portX - pinNumberOffset;
1480
- pinY = portY + pinNumberOffset / 4;
1481
- dominantBaseline = "auto";
1482
- break;
1483
- case "right":
1484
- pinX = portX + pinNumberOffset;
1485
- pinY = portY + pinNumberOffset / 4;
1486
- dominantBaseline = "auto";
1487
- break;
1488
- }
1489
- const [transformedPinX, transformedPinY] = applyToPoint14(transform, [
1490
- pinX,
1491
- pinY
1492
- ]);
1493
- textChildren.push({
1494
- name: "text",
1495
- type: "element",
1496
- attributes: {
1497
- class: "pin-number",
1498
- x: transformedPinX.toString(),
1499
- y: transformedPinY.toString(),
1500
- "text-anchor": "middle",
1501
- "dominant-baseline": dominantBaseline,
1502
- "font-size": (0.15 * componentScale).toString()
1503
- },
1504
- children: [
1505
- {
1506
- type: "text",
1507
- value: pinNumber?.toString() || "",
1508
- name: "",
1509
- attributes: {},
1510
- children: []
1511
- }
1512
- ],
1513
- value: ""
1514
- });
1515
- }
1516
1412
  }
1517
- const componentGroup = {
1518
- name: "g",
1413
+ return svgObjects;
1414
+ };
1415
+
1416
+ // lib/sch/svg-object-fns/create-svg-objects-from-sch-component-with-box.ts
1417
+ import { su as su4 } from "@tscircuit/soup-util";
1418
+ import "schematic-symbols";
1419
+ import "svgson";
1420
+ import { applyToPoint as applyToPoint20 } from "transformation-matrix";
1421
+
1422
+ // lib/sch/svg-object-fns/create-svg-objects-from-sch-port-on-box.ts
1423
+ import "transformation-matrix";
1424
+ import "@tscircuit/soup-util";
1425
+
1426
+ // lib/sch/svg-object-fns/create-svg-objects-for-sch-port-box-line.ts
1427
+ import { applyToPoint as applyToPoint16 } from "transformation-matrix";
1428
+ import { su as su2 } from "@tscircuit/soup-util";
1429
+ var PIN_CIRCLE_RADIUS_MM = 0.02;
1430
+ var createSvgObjectsForSchPortBoxLine = ({
1431
+ schPort,
1432
+ schComponent,
1433
+ transform,
1434
+ circuitJson
1435
+ }) => {
1436
+ const svgObjects = [];
1437
+ const srcPort = su2(circuitJson).source_port.get(schPort.source_port_id);
1438
+ const realEdgePos = {
1439
+ x: schPort.center.x,
1440
+ y: schPort.center.y
1441
+ };
1442
+ const realPinLineLength = schPort.distance_from_component_edge ?? 0.4;
1443
+ switch (schPort.side_of_component) {
1444
+ case "left":
1445
+ realEdgePos.x += realPinLineLength;
1446
+ break;
1447
+ case "right":
1448
+ realEdgePos.x -= realPinLineLength;
1449
+ break;
1450
+ case "top":
1451
+ realEdgePos.y -= realPinLineLength;
1452
+ break;
1453
+ case "bottom":
1454
+ realEdgePos.y += realPinLineLength;
1455
+ break;
1456
+ }
1457
+ const screenSchPortPos = applyToPoint16(transform, schPort.center);
1458
+ const screenRealEdgePos = applyToPoint16(transform, realEdgePos);
1459
+ const realLineEnd = { ...schPort.center };
1460
+ switch (schPort.side_of_component) {
1461
+ case "left":
1462
+ realLineEnd.x += PIN_CIRCLE_RADIUS_MM;
1463
+ break;
1464
+ case "right":
1465
+ realLineEnd.x -= PIN_CIRCLE_RADIUS_MM;
1466
+ break;
1467
+ case "top":
1468
+ realLineEnd.y -= PIN_CIRCLE_RADIUS_MM;
1469
+ break;
1470
+ case "bottom":
1471
+ realLineEnd.y += PIN_CIRCLE_RADIUS_MM;
1472
+ break;
1473
+ }
1474
+ const screenLineEnd = applyToPoint16(transform, realLineEnd);
1475
+ svgObjects.push({
1476
+ name: "line",
1477
+ type: "element",
1478
+ attributes: {
1479
+ class: "component-pin",
1480
+ x1: screenLineEnd.x.toString(),
1481
+ y1: screenLineEnd.y.toString(),
1482
+ x2: screenRealEdgePos.x.toString(),
1483
+ y2: screenRealEdgePos.y.toString(),
1484
+ "stroke-width": `${getSchStrokeSize(transform)}px`
1485
+ },
1519
1486
  value: "",
1487
+ children: []
1488
+ });
1489
+ svgObjects.push({
1490
+ name: "circle",
1520
1491
  type: "element",
1521
1492
  attributes: {
1522
- transform: `translate(${transformedX}, ${transformedY}) rotate(${rotation * 180 / Math.PI}) scale(${componentScale})`
1493
+ class: "component-pin",
1494
+ cx: screenSchPortPos.x.toString(),
1495
+ cy: screenSchPortPos.y.toString(),
1496
+ r: (Math.abs(transform.a) * PIN_CIRCLE_RADIUS_MM).toString(),
1497
+ "stroke-width": `${getSchStrokeSize(transform)}px`
1523
1498
  },
1524
- children: componentChildren
1525
- };
1526
- const textGroup = {
1527
- name: "g",
1528
1499
  value: "",
1500
+ children: []
1501
+ });
1502
+ return svgObjects;
1503
+ };
1504
+
1505
+ // lib/utils/get-unit-vector-from-outside-to-edge.ts
1506
+ var getUnitVectorFromOutsideToEdge = (side) => {
1507
+ switch (side) {
1508
+ case "top":
1509
+ return { x: 0, y: -1 };
1510
+ case "bottom":
1511
+ return { x: 0, y: 1 };
1512
+ case "left":
1513
+ return { x: 1, y: 0 };
1514
+ case "right":
1515
+ return { x: -1, y: 0 };
1516
+ }
1517
+ throw new Error(`Invalid side: ${side}`);
1518
+ };
1519
+
1520
+ // lib/sch/svg-object-fns/create-svg-objects-for-sch-port-pin-number-text.ts
1521
+ import { applyToPoint as applyToPoint17 } from "transformation-matrix";
1522
+ var createSvgObjectsForSchPortPinNumberText = (params) => {
1523
+ const svgObjects = [];
1524
+ const { schPort, schComponent, transform, circuitJson } = params;
1525
+ const realPinNumberPos = {
1526
+ x: schPort.center.x,
1527
+ y: schPort.center.y
1528
+ };
1529
+ if (!schPort.side_of_component) return [];
1530
+ const vecToEdge = getUnitVectorFromOutsideToEdge(schPort.side_of_component);
1531
+ const realPinEdgeDistance = schPort.distance_from_component_edge ?? 0.4;
1532
+ realPinNumberPos.x += vecToEdge.x * realPinEdgeDistance / 2;
1533
+ realPinNumberPos.y += vecToEdge.y * realPinEdgeDistance / 2;
1534
+ const screenPinNumberTextPos = applyToPoint17(transform, realPinNumberPos);
1535
+ if (schPort.side_of_component === "top" || schPort.side_of_component === "bottom") {
1536
+ screenPinNumberTextPos.x -= 2;
1537
+ } else {
1538
+ screenPinNumberTextPos.y -= 2;
1539
+ }
1540
+ svgObjects.push({
1541
+ name: "text",
1529
1542
  type: "element",
1530
- attributes: {},
1531
- children: textChildren
1543
+ attributes: {
1544
+ class: "pin-number",
1545
+ x: screenPinNumberTextPos.x.toString(),
1546
+ y: screenPinNumberTextPos.y.toString(),
1547
+ style: "font-family: sans-serif;",
1548
+ fill: colorMap.schematic.pin_number,
1549
+ "text-anchor": "middle",
1550
+ "dominant-baseline": "auto",
1551
+ "font-size": `${getSchFontSize(transform, "pin_number")}px`,
1552
+ transform: schPort.side_of_component === "top" || schPort.side_of_component === "bottom" ? `rotate(-90 ${screenPinNumberTextPos.x} ${screenPinNumberTextPos.y})` : ""
1553
+ },
1554
+ children: [
1555
+ {
1556
+ type: "text",
1557
+ value: schPort.pin_number?.toString() || "",
1558
+ name: "",
1559
+ attributes: {},
1560
+ children: []
1561
+ }
1562
+ ],
1563
+ value: ""
1564
+ });
1565
+ return svgObjects;
1566
+ };
1567
+
1568
+ // lib/sch/svg-object-fns/create-svg-objects-for-sch-port-pin-label.ts
1569
+ import { applyToPoint as applyToPoint18 } from "transformation-matrix";
1570
+ var LABEL_DIST_FROM_EDGE_MM = 0.1;
1571
+ var createSvgObjectsForSchPortPinLabel = (params) => {
1572
+ const svgObjects = [];
1573
+ const { schPort, schComponent, transform, circuitJson } = params;
1574
+ const realPinNumberPos = {
1575
+ x: schPort.center.x,
1576
+ y: schPort.center.y
1532
1577
  };
1533
- return [componentGroup, textGroup];
1578
+ if (!schPort.side_of_component) return [];
1579
+ const vecToEdge = getUnitVectorFromOutsideToEdge(schPort.side_of_component);
1580
+ const realPinEdgeDistance = schPort.distance_from_component_edge ?? 0.4;
1581
+ realPinNumberPos.x += vecToEdge.x * (realPinEdgeDistance + LABEL_DIST_FROM_EDGE_MM);
1582
+ realPinNumberPos.y += vecToEdge.y * (realPinEdgeDistance + LABEL_DIST_FROM_EDGE_MM);
1583
+ const screenPinNumberTextPos = applyToPoint18(transform, realPinNumberPos);
1584
+ const label = schComponent.port_labels?.[`pin${schPort.pin_number}`];
1585
+ if (!label) return [];
1586
+ svgObjects.push({
1587
+ name: "text",
1588
+ type: "element",
1589
+ attributes: {
1590
+ class: "pin-number",
1591
+ x: screenPinNumberTextPos.x.toString(),
1592
+ y: screenPinNumberTextPos.y.toString(),
1593
+ style: "font-family: sans-serif;",
1594
+ fill: colorMap.schematic.pin_number,
1595
+ "text-anchor": schPort.side_of_component === "left" ? "start" : "end",
1596
+ "dominant-baseline": "middle",
1597
+ "font-size": `${getSchFontSize(transform, "pin_number")}px`,
1598
+ transform: schPort.side_of_component === "top" || schPort.side_of_component === "bottom" ? `rotate(-90 ${screenPinNumberTextPos.x} ${screenPinNumberTextPos.y})` : ""
1599
+ },
1600
+ children: [
1601
+ {
1602
+ type: "text",
1603
+ value: label || "",
1604
+ name: "",
1605
+ attributes: {},
1606
+ children: []
1607
+ }
1608
+ ],
1609
+ value: ""
1610
+ });
1611
+ return svgObjects;
1612
+ };
1613
+
1614
+ // lib/sch/svg-object-fns/create-svg-objects-from-sch-port-on-box.ts
1615
+ var createSvgObjectsFromSchPortOnBox = (params) => {
1616
+ const svgObjects = [];
1617
+ const { schPort, schComponent, transform, circuitJson } = params;
1618
+ svgObjects.push(...createSvgObjectsForSchPortBoxLine(params));
1619
+ svgObjects.push(...createSvgObjectsForSchPortPinNumberText(params));
1620
+ svgObjects.push(...createSvgObjectsForSchPortPinLabel(params));
1621
+ return svgObjects;
1622
+ };
1623
+
1624
+ // lib/sch/svg-object-fns/create-svg-objects-from-sch-component-with-box.ts
1625
+ var createSvgObjectsFromSchematicComponentWithBox = ({
1626
+ component: schComponent,
1627
+ transform,
1628
+ circuitJson
1629
+ }) => {
1630
+ const srcComponent = su4(circuitJson).source_component.get(
1631
+ schComponent.source_component_id
1632
+ );
1633
+ const svgObjects = [];
1634
+ const componentScreenTopLeft = applyToPoint20(transform, {
1635
+ x: schComponent.center.x - schComponent.size.width / 2,
1636
+ y: schComponent.center.y + schComponent.size.height / 2
1637
+ });
1638
+ const componentScreenBottomRight = applyToPoint20(transform, {
1639
+ x: schComponent.center.x + schComponent.size.width / 2,
1640
+ y: schComponent.center.y - schComponent.size.height / 2
1641
+ });
1642
+ const componentScreenWidth = componentScreenBottomRight.x - componentScreenTopLeft.x;
1643
+ const componentScreenHeight = componentScreenBottomRight.y - componentScreenTopLeft.y;
1644
+ svgObjects.push({
1645
+ name: "rect",
1646
+ type: "element",
1647
+ value: "",
1648
+ attributes: {
1649
+ class: "component chip",
1650
+ x: componentScreenTopLeft.x.toString(),
1651
+ y: componentScreenTopLeft.y.toString(),
1652
+ width: componentScreenWidth.toString(),
1653
+ height: componentScreenHeight.toString(),
1654
+ "stroke-width": `${getSchStrokeSize(transform)}px`
1655
+ },
1656
+ children: []
1657
+ });
1658
+ const screenManufacturerNumberPos = applyToPoint20(transform, {
1659
+ x: schComponent.center.x + schComponent.size.width / 2,
1660
+ y: schComponent.center.y + schComponent.size.height / 2 + 0.5
1661
+ // Above the component top edge
1662
+ });
1663
+ const fontSizePx = getSchFontSize(transform, "manufacturer_number");
1664
+ if (srcComponent?.manufacturer_part_number) {
1665
+ svgObjects.push({
1666
+ name: "text",
1667
+ type: "element",
1668
+ attributes: {
1669
+ class: "component-name",
1670
+ x: screenManufacturerNumberPos.x.toString(),
1671
+ y: screenManufacturerNumberPos.y.toString(),
1672
+ "font-family": "sans-serif",
1673
+ "text-anchor": "right",
1674
+ // Center align text
1675
+ "dominant-baseline": "auto",
1676
+ "font-size": `${fontSizePx}px`
1677
+ },
1678
+ children: [
1679
+ {
1680
+ type: "text",
1681
+ value: srcComponent.manufacturer_part_number,
1682
+ name: "",
1683
+ attributes: {},
1684
+ children: []
1685
+ }
1686
+ ],
1687
+ value: ""
1688
+ });
1689
+ }
1690
+ if (srcComponent?.name) {
1691
+ svgObjects.push({
1692
+ name: "text",
1693
+ type: "element",
1694
+ attributes: {
1695
+ class: "component-name",
1696
+ x: screenManufacturerNumberPos.x.toString(),
1697
+ y: (screenManufacturerNumberPos.y + fontSizePx * 1.1).toString(),
1698
+ "font-family": "sans-serif",
1699
+ "text-anchor": "right",
1700
+ // Center align text
1701
+ "dominant-baseline": "auto",
1702
+ "font-size": `${fontSizePx}px`
1703
+ },
1704
+ children: [
1705
+ {
1706
+ type: "text",
1707
+ value: srcComponent.name || "",
1708
+ name: "",
1709
+ attributes: {},
1710
+ children: []
1711
+ }
1712
+ ],
1713
+ value: ""
1714
+ });
1715
+ }
1716
+ const schematicPorts = su4(circuitJson).schematic_port.list({
1717
+ schematic_component_id: schComponent.schematic_component_id
1718
+ });
1719
+ for (const schPort of schematicPorts) {
1720
+ svgObjects.push(
1721
+ ...createSvgObjectsFromSchPortOnBox({
1722
+ schPort,
1723
+ schComponent,
1724
+ transform,
1725
+ circuitJson
1726
+ })
1727
+ );
1728
+ }
1729
+ return svgObjects;
1730
+ };
1731
+
1732
+ // lib/sch/svg-object-fns/create-svg-objects-from-sch-component.ts
1733
+ function createSvgObjectsFromSchematicComponent(params) {
1734
+ const { component } = params;
1735
+ if (component.symbol_name) {
1736
+ return createSvgObjectsFromSchematicComponentWithSymbol(params);
1737
+ }
1738
+ return createSvgObjectsFromSchematicComponentWithBox(params);
1534
1739
  }
1535
1740
 
1536
1741
  // lib/sch/svg-object-fns/create-svg-objects-from-sch-debug-object.ts
1537
- import { applyToPoint as applyToPoint15 } from "transformation-matrix";
1742
+ import { applyToPoint as applyToPoint21 } from "transformation-matrix";
1538
1743
  function createSvgObjectsFromSchDebugObject(debugObject, transform) {
1539
1744
  if (debugObject.shape === "rect") {
1540
- let [screenLeft, screenTop] = applyToPoint15(transform, [
1745
+ let [screenLeft, screenTop] = applyToPoint21(transform, [
1541
1746
  debugObject.center.x - debugObject.size.width / 2,
1542
1747
  debugObject.center.y - debugObject.size.height / 2
1543
1748
  ]);
1544
- let [screenRight, screenBottom] = applyToPoint15(transform, [
1749
+ let [screenRight, screenBottom] = applyToPoint21(transform, [
1545
1750
  debugObject.center.x + debugObject.size.width / 2,
1546
1751
  debugObject.center.y + debugObject.size.height / 2
1547
1752
  ]);
@@ -1551,7 +1756,7 @@ function createSvgObjectsFromSchDebugObject(debugObject, transform) {
1551
1756
  ];
1552
1757
  const width = Math.abs(screenRight - screenLeft);
1553
1758
  const height = Math.abs(screenBottom - screenTop);
1554
- const [screenCenterX, screenCenterY] = applyToPoint15(transform, [
1759
+ const [screenCenterX, screenCenterY] = applyToPoint21(transform, [
1555
1760
  debugObject.center.x,
1556
1761
  debugObject.center.y
1557
1762
  ]);
@@ -1597,11 +1802,11 @@ function createSvgObjectsFromSchDebugObject(debugObject, transform) {
1597
1802
  ];
1598
1803
  }
1599
1804
  if (debugObject.shape === "line") {
1600
- const [screenStartX, screenStartY] = applyToPoint15(transform, [
1805
+ const [screenStartX, screenStartY] = applyToPoint21(transform, [
1601
1806
  debugObject.start.x,
1602
1807
  debugObject.start.y
1603
1808
  ]);
1604
- const [screenEndX, screenEndY] = applyToPoint15(transform, [
1809
+ const [screenEndX, screenEndY] = applyToPoint21(transform, [
1605
1810
  debugObject.end.x,
1606
1811
  debugObject.end.y
1607
1812
  ]);
@@ -1651,7 +1856,7 @@ function createSvgObjectsFromSchDebugObject(debugObject, transform) {
1651
1856
  }
1652
1857
 
1653
1858
  // lib/sch/svg-object-fns/create-svg-objects-from-sch-trace.ts
1654
- import { applyToPoint as applyToPoint16 } from "transformation-matrix";
1859
+ import { applyToPoint as applyToPoint22 } from "transformation-matrix";
1655
1860
  function createSchematicTrace(trace, transform) {
1656
1861
  const edges = trace.edges;
1657
1862
  if (edges.length === 0) return [];
@@ -1665,11 +1870,11 @@ function createSchematicTrace(trace, transform) {
1665
1870
  x: edge.to.x ?? edge.to.center?.x,
1666
1871
  y: edge.to.y ?? edge.to.center?.y
1667
1872
  };
1668
- const [transformedFromX, transformedFromY] = applyToPoint16(transform, [
1873
+ const [transformedFromX, transformedFromY] = applyToPoint22(transform, [
1669
1874
  fromPoint.x,
1670
1875
  fromPoint.y
1671
1876
  ]);
1672
- const [transformedToX, transformedToY] = applyToPoint16(transform, [
1877
+ const [transformedToX, transformedToY] = applyToPoint22(transform, [
1673
1878
  toPoint.x,
1674
1879
  toPoint.y
1675
1880
  ]);
@@ -1686,8 +1891,9 @@ function createSchematicTrace(trace, transform) {
1686
1891
  attributes: {
1687
1892
  class: "trace",
1688
1893
  d: path,
1689
- "stroke-width": (0.02 * Math.abs(transform.a)).toString()
1690
- // Scale stroke width with transform
1894
+ stroke: colorMap.schematic.wire,
1895
+ fill: "none",
1896
+ "stroke-width": `${getSchStrokeSize(transform)}px`
1691
1897
  },
1692
1898
  value: "",
1693
1899
  children: []
@@ -1697,24 +1903,38 @@ function createSchematicTrace(trace, transform) {
1697
1903
 
1698
1904
  // lib/sch/convert-circuit-json-to-schematic-svg.ts
1699
1905
  function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1700
- const bounds = getSchematicBoundsFromCircuitJson(circuitJson);
1701
- const { minX, minY, maxX, maxY } = bounds;
1702
- const padding = 1;
1703
- const circuitWidth = maxX - minX + 2 * padding;
1704
- const circuitHeight = maxY - minY + 2 * padding;
1906
+ const realBounds = getSchematicBoundsFromCircuitJson(circuitJson);
1907
+ const realWidth = realBounds.maxX - realBounds.minX;
1908
+ const realHeight = realBounds.maxY - realBounds.minY;
1705
1909
  const svgWidth = options?.width ?? 1200;
1706
1910
  const svgHeight = options?.height ?? 600;
1707
- const scaleX = svgWidth / circuitWidth;
1708
- const scaleY = svgHeight / circuitHeight;
1709
- const scaleFactor = Math.min(scaleX, scaleY);
1710
- const offsetX = (svgWidth - circuitWidth * scaleFactor) / 2;
1711
- const offsetY = (svgHeight - circuitHeight * scaleFactor) / 2;
1712
- const transform = compose4(
1713
- translate4(
1714
- offsetX - minX * scaleFactor + padding * scaleFactor,
1715
- svgHeight - offsetY + minY * scaleFactor - padding * scaleFactor
1716
- ),
1717
- scale2(scaleFactor, scaleFactor)
1911
+ const circuitAspectRatio = realWidth / realHeight;
1912
+ const containerAspectRatio = svgWidth / svgHeight;
1913
+ let screenPaddingPx;
1914
+ if (circuitAspectRatio > containerAspectRatio) {
1915
+ const newHeight = svgWidth / circuitAspectRatio;
1916
+ screenPaddingPx = {
1917
+ x: 0,
1918
+ y: (svgHeight - newHeight) / 2
1919
+ };
1920
+ } else {
1921
+ const newWidth = svgHeight * circuitAspectRatio;
1922
+ screenPaddingPx = {
1923
+ x: (svgWidth - newWidth) / 2,
1924
+ y: 0
1925
+ };
1926
+ }
1927
+ const transform = fromTriangles(
1928
+ [
1929
+ { x: realBounds.minX, y: realBounds.maxY },
1930
+ { x: realBounds.maxX, y: realBounds.maxY },
1931
+ { x: realBounds.maxX, y: realBounds.minY }
1932
+ ],
1933
+ [
1934
+ { x: screenPaddingPx.x, y: screenPaddingPx.y },
1935
+ { x: svgWidth - screenPaddingPx.x, y: screenPaddingPx.y },
1936
+ { x: svgWidth - screenPaddingPx.x, y: svgHeight - screenPaddingPx.y }
1937
+ ]
1718
1938
  );
1719
1939
  const svgChildren = [];
1720
1940
  svgChildren.push({
@@ -1732,7 +1952,9 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1732
1952
  });
1733
1953
  if (options?.grid) {
1734
1954
  const gridConfig = typeof options.grid === "object" ? options.grid : {};
1735
- svgChildren.push(drawSchematicGrid({ bounds, transform, ...gridConfig }));
1955
+ svgChildren.push(
1956
+ drawSchematicGrid({ bounds: realBounds, transform, ...gridConfig })
1957
+ );
1736
1958
  }
1737
1959
  if (options?.labeledPoints) {
1738
1960
  svgChildren.push(
@@ -1752,7 +1974,7 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1752
1974
  );
1753
1975
  } else if (elm.type === "schematic_component") {
1754
1976
  schComponentSvgs.push(
1755
- ...createSchematicComponent({
1977
+ ...createSvgObjectsFromSchematicComponent({
1756
1978
  component: elm,
1757
1979
  transform,
1758
1980
  circuitJson
@@ -1780,17 +2002,19 @@ function convertCircuitJsonToSchematicSvg(circuitJson, options) {
1780
2002
  children: [
1781
2003
  {
1782
2004
  type: "text",
2005
+ // DO NOT USE THESE CLASSES!!!!
2006
+ // PUT STYLES IN THE SVG OBJECTS THEMSELVES
1783
2007
  value: `
1784
2008
  .boundary { fill: ${colorMap.schematic.background}; }
1785
- .schematic-boundary { fill: none; stroke: #fff; stroke-width: 0.3; }
2009
+ .schematic-boundary { fill: none; stroke: #fff; }
1786
2010
  .component { fill: none; stroke: ${colorMap.schematic.component_outline}; }
1787
2011
  .chip { fill: ${colorMap.schematic.component_body}; stroke: ${colorMap.schematic.component_outline}; }
1788
2012
  .component-pin { fill: none; stroke: ${colorMap.schematic.component_outline}; }
1789
- .trace { stroke: ${colorMap.schematic.wire}; stroke-width: ${0.02 * scaleFactor}; fill: none; }
1790
- .text { font-family: Arial, sans-serif; font-size: ${0.2 * scaleFactor}px; fill: ${colorMap.schematic.wire}; }
1791
- .pin-number { font-size: ${0.15 * scaleFactor}px; fill: ${colorMap.schematic.pin_number}; }
2013
+ .trace { stroke: ${colorMap.schematic.wire}; }
2014
+ .text { font-family: sans-serif; fill: ${colorMap.schematic.wire}; }
2015
+ .pin-number { fill: ${colorMap.schematic.pin_number}; }
1792
2016
  .port-label { fill: ${colorMap.schematic.reference}; }
1793
- .component-name { font-size: ${0.25 * scaleFactor}px; fill: ${colorMap.schematic.reference}; }
2017
+ .component-name { fill: ${colorMap.schematic.reference}; }
1794
2018
  `,
1795
2019
  name: "",
1796
2020
  attributes: {},