@tscircuit/circuit-json-util 0.0.53 → 0.0.55

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
@@ -3,8 +3,7 @@ import * as Soup from "circuit-json";
3
3
 
4
4
  // lib/subtree.ts
5
5
  function connect(map, a, b) {
6
- if (!a || !b)
7
- return;
6
+ if (!a || !b) return;
8
7
  let setA = map.get(a);
9
8
  if (!setA) {
10
9
  setA = /* @__PURE__ */ new Set();
@@ -19,8 +18,7 @@ function connect(map, a, b) {
19
18
  setB.add(a);
20
19
  }
21
20
  function buildSubtree(soup, opts) {
22
- if (!opts.subcircuit_id && !opts.source_group_id)
23
- return [...soup];
21
+ if (!opts.subcircuit_id && !opts.source_group_id) return [...soup];
24
22
  const idMap = /* @__PURE__ */ new Map();
25
23
  for (const elm of soup) {
26
24
  const idVal = elm[`${elm.type}_id`];
@@ -32,6 +30,7 @@ function buildSubtree(soup, opts) {
32
30
  for (const elm of soup) {
33
31
  const entries = Object.entries(elm);
34
32
  for (const [key, val] of entries) {
33
+ if (key === "parent_source_group_id") continue;
35
34
  if (key.endsWith("_id") && typeof val === "string") {
36
35
  const other = idMap.get(val);
37
36
  connect(adj, elm, other);
@@ -58,8 +57,7 @@ function buildSubtree(soup, opts) {
58
57
  while (queue.length > 0) {
59
58
  const elm = queue.shift();
60
59
  const neighbors = adj.get(elm);
61
- if (!neighbors)
62
- continue;
60
+ if (!neighbors) continue;
63
61
  for (const n of neighbors) {
64
62
  if (!included.has(n)) {
65
63
  included.add(n);
@@ -83,8 +81,7 @@ var cju = (circuitJsonInput, options = {}) => {
83
81
  for (const elm of circuitJson) {
84
82
  const type = elm.type;
85
83
  const idVal = elm[`${type}_id`];
86
- if (!idVal)
87
- continue;
84
+ if (!idVal) continue;
88
85
  const idNum = Number.parseInt(idVal.split("_").pop());
89
86
  if (!Number.isNaN(idNum)) {
90
87
  internalStore.counts[type] = Math.max(
@@ -128,8 +125,7 @@ var cju = (circuitJsonInput, options = {}) => {
128
125
  const joiner = circuitJson.find(
129
126
  (e) => e.type === join_type && e[join_key] === using[join_key]
130
127
  );
131
- if (!joiner)
132
- return null;
128
+ if (!joiner) return null;
133
129
  return circuitJson.find(
134
130
  (e) => e.type === component_type && e[`${component_type}_id`] === joiner[`${component_type}_id`]
135
131
  );
@@ -167,8 +163,7 @@ var cju = (circuitJsonInput, options = {}) => {
167
163
  const elm = circuitJson.find(
168
164
  (e) => e[`${component_type}_id`] === id
169
165
  );
170
- if (!elm)
171
- return;
166
+ if (!elm) return;
172
167
  circuitJson.splice(circuitJson.indexOf(elm), 1);
173
168
  internalStore.editCount++;
174
169
  },
@@ -176,8 +171,7 @@ var cju = (circuitJsonInput, options = {}) => {
176
171
  const elm = circuitJson.find(
177
172
  (e) => e.type === component_type && e[`${component_type}_id`] === id
178
173
  );
179
- if (!elm)
180
- return null;
174
+ if (!elm) return null;
181
175
  Object.assign(elm, newProps);
182
176
  internalStore.editCount++;
183
177
  return elm;
@@ -192,15 +186,12 @@ var cju = (circuitJsonInput, options = {}) => {
192
186
  const source_component = circuitJson.find(
193
187
  (e) => e.type === "source_component" && e.name === component_name
194
188
  );
195
- if (!source_component)
196
- return null;
189
+ if (!source_component) return null;
197
190
  const source_port = circuitJson.find(
198
191
  (e) => e.type === "source_port" && e.source_component_id === source_component.source_component_id && (e.name === port_selector || (e.port_hints ?? []).includes(port_selector))
199
192
  );
200
- if (!source_port)
201
- return null;
202
- if (component_type === "source_port")
203
- return source_port;
193
+ if (!source_port) return null;
194
+ if (component_type === "source_port") return source_port;
204
195
  if (component_type === "pcb_port") {
205
196
  return circuitJson.find(
206
197
  (e) => e.type === "pcb_port" && e.source_port_id === source_port.source_port_id
@@ -239,8 +230,7 @@ var cjuIndexed = (soup, options = {}) => {
239
230
  for (const elm of soup) {
240
231
  const type = elm.type;
241
232
  const idVal = elm[`${type}_id`];
242
- if (!idVal)
243
- continue;
233
+ if (!idVal) continue;
244
234
  const idNum = Number.parseInt(idVal.split("_").pop() || "");
245
235
  if (!Number.isNaN(idNum)) {
246
236
  internalStore.counts[type] = Math.max(
@@ -365,8 +355,7 @@ var cjuIndexed = (soup, options = {}) => {
365
355
  if (relationMap) {
366
356
  const relatedElements = relationMap.get(using[join_key]) || [];
367
357
  const joiner2 = relatedElements.find((e) => e.type === join_type);
368
- if (!joiner2)
369
- return null;
358
+ if (!joiner2) return null;
370
359
  const joinerId = joiner2[`${component_type}_id`];
371
360
  if (indexConfig.byId && internalStore.indexes.byId) {
372
361
  return internalStore.indexes.byId.get(
@@ -387,8 +376,7 @@ var cjuIndexed = (soup, options = {}) => {
387
376
  const joiner = soup.find(
388
377
  (e) => e.type === join_type && e[join_key] === using[join_key]
389
378
  );
390
- if (!joiner)
391
- return null;
379
+ if (!joiner) return null;
392
380
  return soup.find(
393
381
  (e) => e.type === component_type && e[`${component_type}_id`] === joiner[`${component_type}_id`]
394
382
  ) || null;
@@ -527,8 +515,7 @@ var cjuIndexed = (soup, options = {}) => {
527
515
  } else {
528
516
  elm = soup.find((e) => e[`${component_type}_id`] === id);
529
517
  }
530
- if (!elm)
531
- return;
518
+ if (!elm) return;
532
519
  const elmIndex = soup.indexOf(elm);
533
520
  if (elmIndex >= 0) {
534
521
  soup.splice(elmIndex, 1);
@@ -605,8 +592,7 @@ var cjuIndexed = (soup, options = {}) => {
605
592
  (e) => e.type === component_type && e[`${component_type}_id`] === id
606
593
  );
607
594
  }
608
- if (!elm)
609
- return null;
595
+ if (!elm) return null;
610
596
  if (indexConfig.byRelation && internalStore.indexes.byRelation) {
611
597
  const elementEntries = Object.entries(elm);
612
598
  for (const [key, value] of elementEntries) {
@@ -722,13 +708,11 @@ var cjuIndexed = (soup, options = {}) => {
722
708
  const source_component = soup.find(
723
709
  (e) => e.type === "source_component" && e.name === component_name
724
710
  );
725
- if (!source_component)
726
- return null;
711
+ if (!source_component) return null;
727
712
  const source_port = soup.find(
728
713
  (e) => e.type === "source_port" && e.source_component_id === source_component.source_component_id && (e.name === port_selector || (e.port_hints ?? []).includes(port_selector))
729
714
  );
730
- if (!source_port)
731
- return null;
715
+ if (!source_port) return null;
732
716
  if (component_type === "source_port")
733
717
  return source_port;
734
718
  if (component_type === "pcb_port") {
@@ -757,53 +741,33 @@ import { applyToPoint, decomposeTSR } from "transformation-matrix";
757
741
 
758
742
  // lib/direction-to-vec.ts
759
743
  var directionToVec = (direction) => {
760
- if (direction === "up")
761
- return { x: 0, y: 1 };
762
- else if (direction === "down")
763
- return { x: 0, y: -1 };
764
- else if (direction === "left")
765
- return { x: -1, y: 0 };
766
- else if (direction === "right")
767
- return { x: 1, y: 0 };
768
- else
769
- throw new Error("Invalid direction");
744
+ if (direction === "up") return { x: 0, y: 1 };
745
+ else if (direction === "down") return { x: 0, y: -1 };
746
+ else if (direction === "left") return { x: -1, y: 0 };
747
+ else if (direction === "right") return { x: 1, y: 0 };
748
+ else throw new Error("Invalid direction");
770
749
  };
771
750
  var vecToDirection = ({ x, y }) => {
772
- if (x > y)
773
- y = 0;
774
- if (y > x)
775
- x = 0;
776
- if (x > 0 && y === 0)
777
- return "right";
778
- else if (x < 0 && y === 0)
779
- return "left";
780
- else if (x === 0 && y > 0)
781
- return "up";
782
- else if (x === 0 && y < 0)
783
- return "down";
784
- else
785
- throw new Error(`Invalid vector for direction conversion (${x}, ${y})`);
751
+ if (x > y) y = 0;
752
+ if (y > x) x = 0;
753
+ if (x > 0 && y === 0) return "right";
754
+ else if (x < 0 && y === 0) return "left";
755
+ else if (x === 0 && y > 0) return "up";
756
+ else if (x === 0 && y < 0) return "down";
757
+ else throw new Error(`Invalid vector for direction conversion (${x}, ${y})`);
786
758
  };
787
759
  var rotateClockwise = (direction) => {
788
- if (direction === "up")
789
- return "right";
790
- else if (direction === "right")
791
- return "down";
792
- else if (direction === "down")
793
- return "left";
794
- else if (direction === "left")
795
- return "up";
760
+ if (direction === "up") return "right";
761
+ else if (direction === "right") return "down";
762
+ else if (direction === "down") return "left";
763
+ else if (direction === "left") return "up";
796
764
  throw new Error(`Invalid direction: ${direction}`);
797
765
  };
798
766
  var rotateCounterClockwise = (direction) => {
799
- if (direction === "up")
800
- return "left";
801
- else if (direction === "left")
802
- return "down";
803
- else if (direction === "down")
804
- return "right";
805
- else if (direction === "right")
806
- return "up";
767
+ if (direction === "up") return "left";
768
+ else if (direction === "left") return "down";
769
+ else if (direction === "down") return "right";
770
+ else if (direction === "right") return "up";
807
771
  throw new Error(`Invalid direction: ${direction}`);
808
772
  };
809
773
  var rotateDirection = (direction, num90DegreeClockwiseTurns) => {
@@ -818,25 +782,17 @@ var rotateDirection = (direction, num90DegreeClockwiseTurns) => {
818
782
  return direction;
819
783
  };
820
784
  var oppositeDirection = (direction) => {
821
- if (direction === "up")
822
- return "down";
823
- else if (direction === "down")
824
- return "up";
825
- else if (direction === "left")
826
- return "right";
827
- else if (direction === "right")
828
- return "left";
785
+ if (direction === "up") return "down";
786
+ else if (direction === "down") return "up";
787
+ else if (direction === "left") return "right";
788
+ else if (direction === "right") return "left";
829
789
  throw new Error(`Invalid direction: ${direction}`);
830
790
  };
831
791
  var oppositeSide = (sideOrDir) => {
832
- if (sideOrDir === "top" || sideOrDir === "up")
833
- return "bottom";
834
- else if (sideOrDir === "bottom" || sideOrDir === "down")
835
- return "top";
836
- else if (sideOrDir === "left")
837
- return "right";
838
- else if (sideOrDir === "right")
839
- return "left";
792
+ if (sideOrDir === "top" || sideOrDir === "up") return "bottom";
793
+ else if (sideOrDir === "bottom" || sideOrDir === "down") return "top";
794
+ else if (sideOrDir === "left") return "right";
795
+ else if (sideOrDir === "right") return "left";
840
796
  throw new Error(`Invalid sideOrDir: ${sideOrDir}`);
841
797
  };
842
798
 
@@ -961,6 +917,7 @@ var applySelectorAST = (elements, selectorAST) => {
961
917
  case "complex": {
962
918
  switch (selectorAST.combinator) {
963
919
  case " ":
920
+ // TODO technically should do a deep search
964
921
  case ">": {
965
922
  const { left, right } = selectorAST;
966
923
  if (left.type === "class" || left.type === "type") {
@@ -1048,16 +1005,13 @@ function getReadableNameForPcbTrace(soup, pcb_trace_id) {
1048
1005
  }
1049
1006
  function getComponentAndPortInfo(pcb_port_id) {
1050
1007
  const pcbPort = cju(soup).pcb_port.get(pcb_port_id);
1051
- if (!pcbPort)
1052
- return null;
1008
+ if (!pcbPort) return null;
1053
1009
  const pcbComponent = cju(soup).pcb_component.get(pcbPort.pcb_component_id);
1054
- if (!pcbComponent)
1055
- return null;
1010
+ if (!pcbComponent) return null;
1056
1011
  const sourceComponent = cju(soup).source_component.get(
1057
1012
  pcbComponent.source_component_id
1058
1013
  );
1059
- if (!sourceComponent)
1060
- return null;
1014
+ if (!sourceComponent) return null;
1061
1015
  const sourcePort = cju(soup).source_port.get(pcbPort.source_port_id);
1062
1016
  const portHint = sourcePort?.port_hints ? sourcePort.port_hints[1] : "";
1063
1017
  return {
@@ -1119,8 +1073,7 @@ function getReadableNameForPcbSmtpad(soup, pcb_smtpad_id) {
1119
1073
  var getReadableNameForElement = (soup, elm) => {
1120
1074
  if (typeof elm === "string") {
1121
1075
  const elmObj = getElementById(soup, elm);
1122
- if (!elmObj)
1123
- `unknown (could not find element with id ${elm})`;
1076
+ if (!elmObj) `unknown (could not find element with id ${elm})`;
1124
1077
  return getReadableNameForElement(soup, elmObj);
1125
1078
  }
1126
1079
  switch (elm.type) {
@@ -1144,8 +1097,7 @@ var getBoundsOfPcbElements = (elements) => {
1144
1097
  let maxX = Number.NEGATIVE_INFINITY;
1145
1098
  let maxY = Number.NEGATIVE_INFINITY;
1146
1099
  for (const elm of elements) {
1147
- if (!elm.type.startsWith("pcb_"))
1148
- continue;
1100
+ if (!elm.type.startsWith("pcb_")) continue;
1149
1101
  let centerX;
1150
1102
  let centerY;
1151
1103
  let width;
@@ -1200,8 +1152,7 @@ var getBoundsOfPcbElements = (elements) => {
1200
1152
  // lib/utils/string-hash.ts
1201
1153
  function stringHash(str) {
1202
1154
  let hash = 0;
1203
- if (str.length == 0)
1204
- return hash;
1155
+ if (str.length == 0) return hash;
1205
1156
  for (var i = 0; i < str.length; i++) {
1206
1157
  var char = str.charCodeAt(i);
1207
1158
  hash = (hash << 5) - hash + char;
@@ -1345,8 +1296,7 @@ var getDebugLayoutObject = (lo) => {
1345
1296
  }
1346
1297
  const title = lo.text || lo.name || lo.source?.text || lo.source?.name || "?";
1347
1298
  const content = lo;
1348
- if (x === void 0 || y === void 0)
1349
- return null;
1299
+ if (x === void 0 || y === void 0) return null;
1350
1300
  if (width === void 0) {
1351
1301
  if ("outer_diameter" in lo) {
1352
1302
  width = lo.outer_diameter;
@@ -1405,8 +1355,7 @@ var repositionPcbComponentTo = (circuitJson, pcb_component_id, newCenter) => {
1405
1355
  const pcbComponent = circuitJson.find(
1406
1356
  (e) => e.type === "pcb_component" && e.pcb_component_id === pcb_component_id
1407
1357
  );
1408
- if (!pcbComponent)
1409
- return;
1358
+ if (!pcbComponent) return;
1410
1359
  const currentCenter = "center" in pcbComponent ? pcbComponent.center : { x: pcbComponent.x, y: pcbComponent.y };
1411
1360
  const dx = newCenter.x - currentCenter.x;
1412
1361
  const dy = newCenter.y - currentCenter.y;
@@ -1414,15 +1363,12 @@ var repositionPcbComponentTo = (circuitJson, pcb_component_id, newCenter) => {
1414
1363
  (e) => e.type === "pcb_port" && e.pcb_component_id === pcb_component_id
1415
1364
  ).map((e) => e.pcb_port_id);
1416
1365
  const elementsToMove = circuitJson.filter((elm) => {
1417
- if (elm === pcbComponent)
1418
- return true;
1366
+ if (elm === pcbComponent) return true;
1419
1367
  const anyElm = elm;
1420
- if (anyElm.pcb_component_id === pcb_component_id)
1421
- return true;
1368
+ if (anyElm.pcb_component_id === pcb_component_id) return true;
1422
1369
  if (Array.isArray(anyElm.pcb_component_ids) && anyElm.pcb_component_ids.includes(pcb_component_id))
1423
1370
  return true;
1424
- if (anyElm.pcb_port_id && portIds.includes(anyElm.pcb_port_id))
1425
- return true;
1371
+ if (anyElm.pcb_port_id && portIds.includes(anyElm.pcb_port_id)) return true;
1426
1372
  if (Array.isArray(anyElm.pcb_port_ids) && anyElm.pcb_port_ids.some((id) => portIds.includes(id)))
1427
1373
  return true;
1428
1374
  if (anyElm.start_pcb_port_id && portIds.includes(anyElm.start_pcb_port_id))
@@ -1438,6 +1384,83 @@ var repositionPcbComponentTo = (circuitJson, pcb_component_id, newCenter) => {
1438
1384
  const matrix = translate(dx, dy);
1439
1385
  transformPCBElements(elementsToMove, matrix);
1440
1386
  };
1387
+
1388
+ // lib/getCircuitJsonTree.ts
1389
+ var getCircuitJsonTree = (circuitJson, opts) => {
1390
+ const groupChildMap = /* @__PURE__ */ new Map();
1391
+ for (const elm of circuitJson) {
1392
+ if (elm.type === "source_group" && elm.parent_source_group_id) {
1393
+ const parentId = elm.parent_source_group_id;
1394
+ const childId = elm.source_group_id;
1395
+ const children = groupChildMap.get(parentId) ?? [];
1396
+ children.push(childId);
1397
+ groupChildMap.set(parentId, children);
1398
+ if (!groupChildMap.has(childId)) {
1399
+ groupChildMap.set(childId, []);
1400
+ }
1401
+ }
1402
+ }
1403
+ const groupNodes = /* @__PURE__ */ new Map();
1404
+ const getNextGroupId = () => {
1405
+ for (const [parentId, children] of groupChildMap) {
1406
+ if (groupNodes.has(parentId)) continue;
1407
+ if (children.every((childId) => groupNodes.has(childId))) {
1408
+ return parentId;
1409
+ }
1410
+ }
1411
+ return null;
1412
+ };
1413
+ let lastGroupId = null;
1414
+ while (true) {
1415
+ const nextGroupId = getNextGroupId();
1416
+ if (!nextGroupId) break;
1417
+ const sourceGroup = circuitJson.find(
1418
+ (elm) => elm.type === "source_group" && elm.source_group_id === nextGroupId
1419
+ );
1420
+ const node = {
1421
+ nodeType: "group",
1422
+ sourceGroup,
1423
+ otherChildElements: [],
1424
+ childNodes: [
1425
+ ...groupChildMap.get(nextGroupId)?.map((childId) => groupNodes.get(childId)) ?? [],
1426
+ ...circuitJson.filter(
1427
+ (elm) => elm.type === "source_component" && elm.source_group_id === nextGroupId
1428
+ ).map((elm) => {
1429
+ return {
1430
+ nodeType: "component",
1431
+ sourceComponent: elm,
1432
+ childNodes: [],
1433
+ otherChildElements: []
1434
+ // TODO
1435
+ };
1436
+ })
1437
+ ]
1438
+ };
1439
+ groupNodes.set(nextGroupId, node);
1440
+ lastGroupId = nextGroupId;
1441
+ }
1442
+ if (!lastGroupId) {
1443
+ throw new Error(
1444
+ "Could not compute circuit tree because no groups were processed"
1445
+ );
1446
+ }
1447
+ return groupNodes.get(opts?.source_group_id ?? lastGroupId);
1448
+ };
1449
+
1450
+ // lib/getStringFromCircuitJsonTree.ts
1451
+ var getStringFromCircuitJsonTree = (circuitJsonTree, indent = 0) => {
1452
+ if (circuitJsonTree.nodeType === "component") {
1453
+ return `${" ".repeat(indent)}${circuitJsonTree.sourceComponent?.name ?? circuitJsonTree.sourceComponent?.source_component_id}`;
1454
+ }
1455
+ const lines = [];
1456
+ lines.push(
1457
+ `${" ".repeat(indent)}${circuitJsonTree.sourceGroup?.name ?? circuitJsonTree.sourceGroup?.source_group_id}`
1458
+ );
1459
+ for (const child of circuitJsonTree.childNodes) {
1460
+ lines.push(getStringFromCircuitJsonTree(child, indent + 2));
1461
+ }
1462
+ return lines.join("\n");
1463
+ };
1441
1464
  export {
1442
1465
  applySelector,
1443
1466
  applySelectorAST,
@@ -1447,6 +1470,7 @@ export {
1447
1470
  directionToVec,
1448
1471
  findBoundsAndCenter,
1449
1472
  getBoundsOfPcbElements,
1473
+ getCircuitJsonTree,
1450
1474
  getElementById,
1451
1475
  getElementId,
1452
1476
  getPrimaryId,
@@ -1454,6 +1478,7 @@ export {
1454
1478
  getReadableNameForPcbPort,
1455
1479
  getReadableNameForPcbSmtpad,
1456
1480
  getReadableNameForPcbTrace,
1481
+ getStringFromCircuitJsonTree,
1457
1482
  oppositeDirection,
1458
1483
  oppositeSide,
1459
1484
  repositionPcbComponentTo,