altium-to-circuit-json 0.0.13 → 0.0.15

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
@@ -695,6 +695,11 @@ var CARDINAL_DIRECTIONS = [
695
695
  ];
696
696
  var SYMBOL_CATALOG = symbols;
697
697
  var SYMBOL_NAMES = Object.keys(SYMBOL_CATALOG);
698
+ var INLINE_NET_LABEL_COLOR = "rgb(132, 0, 0)";
699
+ var DEFAULT_INLINE_NET_LABEL_FONT_SIZE = 0.18;
700
+ var MIN_INLINE_NET_LABEL_FONT_SIZE = 0.1;
701
+ var INLINE_NET_LABEL_CHARACTER_WIDTH = 0.12;
702
+ var INLINE_NET_LABEL_HORIZONTAL_PADDING = 0.12;
698
703
  function convertSemanticSchematic(document, options) {
699
704
  const handledRecords = /* @__PURE__ */ new Set();
700
705
  const elements = [];
@@ -742,6 +747,12 @@ function convertComponents(params) {
742
747
  (record) => record.recordKind === "2" && (!isPinHidden(record) || options.includeHidden === true)
743
748
  );
744
749
  if (pins.length === 0) continue;
750
+ const visibleSymbolLabels = new Set(
751
+ visibleOwnedRecords.filter((record) => record.recordKind === "4").flatMap((record) => {
752
+ const text = record.getDecoded("TEXT")?.trim().toUpperCase();
753
+ return text ? [text] : [];
754
+ })
755
+ );
745
756
  const designator = findOwnedText(ownedRecords, "34", "Designator") ?? componentRecord.getDecoded("DESIGNATOR") ?? `U${componentIndex}`;
746
757
  const value = findOwnedText(ownedRecords, "41", "Comment") ?? componentRecord.getDecoded("COMMENT") ?? componentRecord.getDecoded("DESIGNITEMID") ?? componentRecord.getDecoded("LIBREFERENCE") ?? "";
747
758
  const libraryReference = componentRecord.getDecoded("LIBREFERENCE") ?? componentRecord.getDecoded("DESIGNITEMID") ?? designator;
@@ -774,7 +785,8 @@ function convertComponents(params) {
774
785
  pin,
775
786
  pinIndex,
776
787
  schematicComponentId,
777
- sourceComponentId
788
+ sourceComponentId,
789
+ visibleSymbolLabels
778
790
  })
779
791
  );
780
792
  const bodyBounds = getComponentBodyBounds(
@@ -863,7 +875,8 @@ function convertComponentPin(params) {
863
875
  pin,
864
876
  pinIndex,
865
877
  schematicComponentId,
866
- sourceComponentId
878
+ sourceComponentId,
879
+ visibleSymbolLabels
867
880
  } = params;
868
881
  const recordIndex = document.records.indexOf(pin);
869
882
  const pinConglomerate = pin.getNumber("PINCONGLOMERATE");
@@ -894,10 +907,13 @@ function convertComponentPin(params) {
894
907
  ...pinNumber === void 0 ? {} : { pin_number: pinNumber }
895
908
  };
896
909
  const electricalType = pin.getNumber("ELECTRICAL");
910
+ const normalizedName = name.trim().toUpperCase();
911
+ const functionalName = normalizedName.replace(/\d+$/u, "");
912
+ const showName = pinConglomerate === void 0 || (pinConglomerate & 8) !== 0 || visibleSymbolLabels.has(normalizedName) || visibleSymbolLabels.has(functionalName);
897
913
  const schematicPort = {
898
914
  type: "schematic_port",
899
915
  center: scalePoint(terminalPoint, options.scale),
900
- display_pin_label: name,
916
+ ...showName && name ? { display_pin_label: name } : {},
901
917
  distance_from_component_edge: pinLength * options.scale,
902
918
  facing_direction: direction,
903
919
  is_connected: false,
@@ -1030,7 +1046,7 @@ function buildSemanticNetGraph(document, convertedPorts) {
1030
1046
  if (!["4", "17", "18", "25", "29"].includes(record.recordKind ?? "")) {
1031
1047
  return [];
1032
1048
  }
1033
- const point = getLocation(record);
1049
+ const point = record.recordKind === "18" ? getPortConnectionGeometry(record, segments)?.anchor : getLocation(record);
1034
1050
  return point ? [{ point, record }] : [];
1035
1051
  });
1036
1052
  const positionedPins = convertedPorts.map(({ point, record }) => ({
@@ -1201,6 +1217,8 @@ function convertConnectivity(params) {
1201
1217
  } = params;
1202
1218
  const sourceNetIdByName = /* @__PURE__ */ new Map();
1203
1219
  const sourceNetIdByRecord = /* @__PURE__ */ new Map();
1220
+ const sourcePortCountByRecord = /* @__PURE__ */ new Map();
1221
+ const sourceTraceIdByRecord = /* @__PURE__ */ new Map();
1204
1222
  const schematicTraceIdByRecord = /* @__PURE__ */ new Map();
1205
1223
  const portsByPoint = groupByPoint(convertedPorts);
1206
1224
  for (const [netIndex, net] of graph.nets.entries()) {
@@ -1226,6 +1244,10 @@ function convertConnectivity(params) {
1226
1244
  if (firstSourceNetId) sourceNetIdByRecord.set(record, firstSourceNetId);
1227
1245
  }
1228
1246
  const sourceTraceId = `source_trace_altium_${netIndex}`;
1247
+ for (const record of net.records) {
1248
+ sourcePortCountByRecord.set(record, connectedPorts.length);
1249
+ sourceTraceIdByRecord.set(record, sourceTraceId);
1250
+ }
1229
1251
  const sourceTrace = {
1230
1252
  type: "source_trace",
1231
1253
  connected_source_net_ids: sourceNetIds,
@@ -1324,19 +1346,22 @@ function convertConnectivity(params) {
1324
1346
  return {
1325
1347
  schematicTraceIdByRecord,
1326
1348
  sourceNetIdByName,
1327
- sourceNetIdByRecord
1349
+ sourceNetIdByRecord,
1350
+ sourcePortCountByRecord,
1351
+ sourceTraceIdByRecord
1328
1352
  };
1329
1353
  }
1330
1354
  function getEquivalentPortPrunedSegmentKeys(params) {
1331
1355
  const { convertedPorts, net, wires } = params;
1332
1356
  const hiddenPortPoints = convertedPorts.filter((port) => !port.isSchematicVisible).map((port) => port.point);
1333
1357
  if (hiddenPortPoints.length === 0) return /* @__PURE__ */ new Set();
1358
+ const wireSegments = getWireSegments(wires);
1334
1359
  const protectedPointKeys = new Set(
1335
1360
  convertedPorts.filter((port) => port.isSchematicVisible).map((port) => pointKey(port.point))
1336
1361
  );
1337
1362
  for (const record of net.records) {
1338
1363
  if (!["4", "17", "18", "25"].includes(record.recordKind ?? "")) continue;
1339
- const location = getLocation(record);
1364
+ const location = record.recordKind === "18" ? getPortConnectionGeometry(record, wireSegments)?.anchor : getLocation(record);
1340
1365
  if (location) protectedPointKeys.add(pointKey(location));
1341
1366
  }
1342
1367
  const segments = /* @__PURE__ */ new Map();
@@ -1421,6 +1446,9 @@ function convertNetLabels(params) {
1421
1446
  options,
1422
1447
  semanticNetGraph: graph
1423
1448
  } = params;
1449
+ const allWireSegments = getWireSegments(
1450
+ document.records.filter((record) => record.recordKind === "27")
1451
+ );
1424
1452
  for (const [recordIndex, record] of document.records.entries()) {
1425
1453
  const recordKind = record.recordKind;
1426
1454
  if (!recordKind || !["4", "17", "18", "25"].includes(recordKind)) {
@@ -1431,10 +1459,30 @@ function convertNetLabels(params) {
1431
1459
  continue;
1432
1460
  }
1433
1461
  const name = record.recordKind === "18" ? record.getDecoded("NAME") : record.getDecoded("TEXT");
1434
- const location = getLocation(record);
1462
+ const portConnection = recordKind === "18" ? getPortConnectionGeometry(record, allWireSegments) : void 0;
1463
+ const location = portConnection?.anchor ?? getLocation(record);
1435
1464
  if (!name || !location) continue;
1436
- const wire = graph.getConnectedWiresForRecord(record)[0];
1465
+ const connectedWires = graph.getConnectedWiresForRecord(record);
1466
+ const wire = connectedWires[0];
1437
1467
  if (recordKind === "4" && !wire) continue;
1468
+ const sourceTraceId = connectivity.sourceTraceIdByRecord.get(record);
1469
+ const shouldRenderInline = Boolean(sourceTraceId) && (recordKind === "25" || recordKind === "18" && (record.getNumber("IOTYPE") ?? 0) === 0 && connectivity.sourcePortCountByRecord.get(record) === 2 && !isPowerNet(name));
1470
+ if (shouldRenderInline && sourceTraceId) {
1471
+ handledRecords.add(record);
1472
+ elements.push(
1473
+ createInlineNetLabelText({
1474
+ connectedWires,
1475
+ document,
1476
+ location,
1477
+ name,
1478
+ options,
1479
+ record,
1480
+ recordIndex,
1481
+ sourceTraceId
1482
+ })
1483
+ );
1484
+ continue;
1485
+ }
1438
1486
  handledRecords.add(record);
1439
1487
  const sourceNetId = connectivity.sourceNetIdByRecord.get(record) ?? getOrCreateSourceNet({
1440
1488
  elements,
@@ -1447,7 +1495,9 @@ function convertNetLabels(params) {
1447
1495
  const schematicNetLabel = {
1448
1496
  type: "schematic_net_label",
1449
1497
  anchor_position: scalePoint(location, options.scale),
1450
- anchor_side: record.recordKind === "18" || record.recordKind === "4" ? "left" : directionToOppositeSide(direction),
1498
+ anchor_side: directionToOppositeSide(
1499
+ portConnection?.bodyDirection ?? direction
1500
+ ),
1451
1501
  center: scalePoint(location, options.scale),
1452
1502
  schematic_net_label_id: `schematic_net_label_altium_${recordIndex}`,
1453
1503
  schematic_sheet_id: options.schematicSheetId,
@@ -1459,6 +1509,125 @@ function convertNetLabels(params) {
1459
1509
  elements.push(schematicNetLabel);
1460
1510
  }
1461
1511
  }
1512
+ function createInlineNetLabelText(params) {
1513
+ const {
1514
+ connectedWires,
1515
+ document,
1516
+ location,
1517
+ name,
1518
+ options,
1519
+ record,
1520
+ recordIndex,
1521
+ sourceTraceId
1522
+ } = params;
1523
+ const direction = getInlineNetLabelDirection(record, location, connectedWires);
1524
+ const scaledLocation = scalePoint(location, options.scale);
1525
+ const fontSize = getInlineNetLabelFontSize(record, document, options.scale);
1526
+ const fontScale = fontSize / DEFAULT_INLINE_NET_LABEL_FONT_SIZE;
1527
+ const textWidth = (name.length * INLINE_NET_LABEL_CHARACTER_WIDTH + INLINE_NET_LABEL_HORIZONTAL_PADDING) * fontScale;
1528
+ const isVertical = direction === "up" || direction === "down";
1529
+ const directionSign = direction === "left" || direction === "down" ? -1 : 1;
1530
+ const isTerminalPort = record.recordKind === "18";
1531
+ const anchor = isTerminalPort ? directionSign > 0 ? "left" : "right" : "center";
1532
+ const position = isTerminalPort ? isVertical ? {
1533
+ x: scaledLocation.x - fontSize / 2,
1534
+ y: scaledLocation.y
1535
+ } : {
1536
+ x: scaledLocation.x,
1537
+ y: scaledLocation.y + fontSize / 2
1538
+ } : isVertical ? {
1539
+ x: scaledLocation.x - fontSize / 2,
1540
+ y: scaledLocation.y + directionSign * textWidth / 2
1541
+ } : {
1542
+ x: scaledLocation.x + directionSign * textWidth / 2,
1543
+ y: scaledLocation.y + fontSize / 2
1544
+ };
1545
+ return {
1546
+ type: "schematic_text",
1547
+ anchor,
1548
+ color: INLINE_NET_LABEL_COLOR,
1549
+ font_size: fontSize,
1550
+ position,
1551
+ rotation: isVertical ? -90 : 0,
1552
+ schematic_sheet_id: options.schematicSheetId,
1553
+ schematic_text_id: `schematic_inline_net_label_altium_${recordIndex}`,
1554
+ source_trace_id: sourceTraceId,
1555
+ text: name
1556
+ };
1557
+ }
1558
+ function getInlineNetLabelFontSize(record, document, scale) {
1559
+ const fontId = Math.max(
1560
+ Math.round(Number(record.getCaseInsensitive("FONTID") ?? 1)),
1561
+ 1
1562
+ );
1563
+ const sheetRecord = document.records.find(
1564
+ (candidate) => candidate.recordKind === "31"
1565
+ );
1566
+ const sourceFontSize = Number(
1567
+ sheetRecord?.getCaseInsensitive(`SIZE${fontId}`) ?? DEFAULT_INLINE_NET_LABEL_FONT_SIZE / scale
1568
+ );
1569
+ return Math.min(
1570
+ DEFAULT_INLINE_NET_LABEL_FONT_SIZE,
1571
+ Math.max(MIN_INLINE_NET_LABEL_FONT_SIZE, sourceFontSize * scale)
1572
+ );
1573
+ }
1574
+ function getInlineNetLabelDirection(record, location, connectedWires) {
1575
+ if (record.recordKind !== "18") return getRecordDirection(record);
1576
+ for (const wire of connectedWires) {
1577
+ const points = getSchematicRecordPoints(wire);
1578
+ for (let pointIndex = 1; pointIndex < points.length; pointIndex++) {
1579
+ const start = points[pointIndex - 1];
1580
+ const end = points[pointIndex];
1581
+ if (!start || !end || !isPointOnSegment(location, start, end)) continue;
1582
+ const other = pointsEqual(location, start) ? end : pointsEqual(location, end) ? start : void 0;
1583
+ if (!other) continue;
1584
+ const dx = location.x - other.x;
1585
+ const dy = location.y - other.y;
1586
+ if (Math.abs(dx) >= Math.abs(dy)) return dx < 0 ? "left" : "right";
1587
+ return dy < 0 ? "down" : "up";
1588
+ }
1589
+ }
1590
+ return getRecordDirection(record);
1591
+ }
1592
+ function getWireSegments(wires) {
1593
+ return wires.flatMap((wire) => {
1594
+ const points = getSchematicRecordPoints(wire);
1595
+ const segments = [];
1596
+ for (let pointIndex = 1; pointIndex < points.length; pointIndex++) {
1597
+ const start = points[pointIndex - 1];
1598
+ const end = points[pointIndex];
1599
+ if (start && end) segments.push({ end, start });
1600
+ }
1601
+ return segments;
1602
+ });
1603
+ }
1604
+ function getPortConnectionGeometry(record, wireSegments) {
1605
+ const origin = getLocation(record);
1606
+ if (!origin) return void 0;
1607
+ const originToExtremity = getRecordDirection(record);
1608
+ const directionVector = VECTOR_BY_DIRECTION[originToExtremity];
1609
+ const width = Math.max(record.getNumber("WIDTH") ?? 16, 0);
1610
+ const extremity = {
1611
+ x: origin.x + directionVector.x * width,
1612
+ y: origin.y + directionVector.y * width
1613
+ };
1614
+ const touchesWire = (point) => wireSegments.some(
1615
+ (segment) => isPointOnSegment(point, segment.start, segment.end)
1616
+ );
1617
+ const originConnected = touchesWire(origin);
1618
+ const extremityConnected = touchesWire(extremity);
1619
+ const connectedEnd = record.getNumber("CONNECTEDEND");
1620
+ const connectsAtExtremity = connectedEnd === 2 || connectedEnd !== 1 && connectedEnd !== 3 && !originConnected && extremityConnected;
1621
+ return connectsAtExtremity ? {
1622
+ anchor: extremity,
1623
+ bodyDirection: getOppositeDirection(originToExtremity)
1624
+ } : { anchor: origin, bodyDirection: originToExtremity };
1625
+ }
1626
+ function getOppositeDirection(direction) {
1627
+ if (direction === "up") return "down";
1628
+ if (direction === "down") return "up";
1629
+ return direction === "left" ? "right" : "left";
1630
+ }
1462
1631
  function getOrCreateSourceNet(params) {
1463
1632
  const { elements, name, sourceNetIdByName } = params;
1464
1633
  const normalizedName = name.trim().toUpperCase();