@tscircuit/cli 0.1.1290 → 0.1.1291

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.
@@ -11953,7 +11953,10 @@ var resolveImageFormatSelection = (options) => {
11953
11953
  // cli/build/worker-output-generators.ts
11954
11954
  import fs9 from "node:fs";
11955
11955
  import path12 from "node:path";
11956
- import { convertCircuitJsonToGltf } from "circuit-json-to-gltf";
11956
+ import {
11957
+ convertCircuitJsonToGltf,
11958
+ getBestCameraPosition
11959
+ } from "circuit-json-to-gltf";
11957
11960
  import {
11958
11961
  convertCircuitJsonToPcbSvg,
11959
11962
  convertCircuitJsonToSchematicSvg
@@ -13552,7 +13555,7 @@ var writeImageAssetsFromCircuitJson = async (circuitJson, options) => {
13552
13555
  const circuitJsonWithFileUrls = convertModelUrlsToFileUrls(circuitJson);
13553
13556
  const glbBuffer = await convertCircuitJsonToGltf(circuitJsonWithFileUrls, getCircuitJsonToGltfOptions({ format: "glb" }));
13554
13557
  const glbArrayBuffer = await normalizeToArrayBuffer(glbBuffer);
13555
- const pngBuffer = await renderGLTFToPNGBufferFromGLBBuffer(glbArrayBuffer);
13558
+ const pngBuffer = await renderGLTFToPNGBufferFromGLBBuffer(glbArrayBuffer, getBestCameraPosition(circuitJson));
13556
13559
  fs9.writeFileSync(path12.join(outputDir, "3d.png"), Buffer.from(normalizeToUint8Array(pngBuffer)));
13557
13560
  }
13558
13561
  };
package/dist/cli/main.js CHANGED
@@ -60611,6 +60611,616 @@ var init_dist_VYXUNJDT = __esm(() => {
60611
60611
  ];
60612
60612
  });
60613
60613
 
60614
+ // node_modules/circuit-json-trace-length-analysis/lib/analyze-circuit-json-trace-length.ts
60615
+ class Trace {
60616
+ id;
60617
+ label;
60618
+ connectionType;
60619
+ connectionTarget;
60620
+ connectionTargetPosition;
60621
+ connectedPins;
60622
+ pinPositions;
60623
+ requirements;
60624
+ points;
60625
+ lengthMm;
60626
+ straightLineDistanceMm;
60627
+ sourceTraceId;
60628
+ displayName;
60629
+ constructor(model) {
60630
+ this.id = model.id;
60631
+ this.label = model.label;
60632
+ this.connectionType = model.connectionType;
60633
+ this.connectionTarget = model.connectionTarget;
60634
+ this.connectionTargetPosition = model.connectionTargetPosition;
60635
+ this.connectedPins = model.connectedPins;
60636
+ this.pinPositions = model.pinPositions;
60637
+ this.requirements = model.requirements;
60638
+ this.points = model.points;
60639
+ this.lengthMm = model.lengthMm;
60640
+ this.straightLineDistanceMm = model.straightLineDistanceMm;
60641
+ this.sourceTraceId = model.sourceTraceId;
60642
+ this.displayName = model.displayName;
60643
+ }
60644
+ toString() {
60645
+ const lines = [
60646
+ `<Trace id="${escapeXml(this.id)}" label="${escapeXml(this.label)}" connectionType="${escapeXml(this.connectionType)}" lengthMm="${formatNumber3(this.lengthMm)}" straightLineDistanceMm="${formatNumber3(this.straightLineDistanceMm)}">`,
60647
+ ` <ConnectedPins>`,
60648
+ ...this.connectedPins.map((pin) => ` <Pin ref="${escapeXml(pin.ref)}" />`),
60649
+ ` </ConnectedPins>`,
60650
+ ` <PinPositions>`,
60651
+ ...this.pinPositions.map((pin) => renderPinPosition(pin, " ")),
60652
+ ` </PinPositions>`,
60653
+ renderConnection(this),
60654
+ renderRequirements(this.requirements, " "),
60655
+ `</Trace>`
60656
+ ];
60657
+ if (this.points.length > 0) {
60658
+ lines.splice(lines.length - 1, 0, ` <Path>`, ...this.points.map((point5) => ` <Point x="${formatNumber3(point5.x)}" y="${formatNumber3(point5.y)}" layer="${escapeXml(point5.layer)}" kind="${escapeXml(point5.kind)}" />`), ` </Path>`);
60659
+ }
60660
+ return lines.join(`
60661
+ `);
60662
+ }
60663
+ }
60664
+
60665
+ class TraceLengthAnalysis {
60666
+ requestedTarget;
60667
+ resolvedTarget;
60668
+ targetKind;
60669
+ totalLengthMm;
60670
+ totalStraightLineDistanceMm;
60671
+ traceCount;
60672
+ #traces;
60673
+ constructor(args) {
60674
+ this.requestedTarget = args.requestedTarget;
60675
+ this.resolvedTarget = args.resolvedTarget;
60676
+ this.targetKind = args.targetKind;
60677
+ this.#traces = [...args.traces];
60678
+ this.totalLengthMm = this.#traces.reduce((sum, trace) => sum + trace.lengthMm, 0);
60679
+ this.totalStraightLineDistanceMm = this.#traces.reduce((sum, trace) => sum + trace.straightLineDistanceMm, 0);
60680
+ this.traceCount = this.#traces.length;
60681
+ }
60682
+ listTraces() {
60683
+ return [...this.#traces];
60684
+ }
60685
+ toString() {
60686
+ const lines = [
60687
+ `<TraceLengthAnalysis requestedTarget="${escapeXml(this.requestedTarget)}" resolvedTarget="${escapeXml(this.resolvedTarget)}" targetKind="${escapeXml(this.targetKind)}" traceCount="${this.traceCount}" totalLengthMm="${formatNumber3(this.totalLengthMm)}" totalStraightLineDistanceMm="${formatNumber3(this.totalStraightLineDistanceMm)}">`,
60688
+ ...this.#traces.flatMap((trace) => trace.toString().split(`
60689
+ `).map((line2) => ` ${line2}`)),
60690
+ `</TraceLengthAnalysis>`
60691
+ ];
60692
+ return lines.join(`
60693
+ `);
60694
+ }
60695
+ }
60696
+ function analyzeCircuitJsonTraceLength(circuitJson, options) {
60697
+ const index = new CircuitJsonIndex(circuitJson);
60698
+ const target = resolveTarget(index, options.targetPinOrNet);
60699
+ const traces = target.kind === "pin" ? collectPinTraces(index, target) : collectNetTraces(index, target);
60700
+ return new TraceLengthAnalysis({
60701
+ requestedTarget: target.requestedTarget,
60702
+ resolvedTarget: target.resolvedTarget,
60703
+ targetKind: target.kind,
60704
+ traces
60705
+ });
60706
+ }
60707
+
60708
+ class CircuitJsonIndex {
60709
+ componentsById = new Map;
60710
+ componentsByName = new Map;
60711
+ portsById = new Map;
60712
+ portsByComponentId = new Map;
60713
+ netsById = new Map;
60714
+ netsByName = new Map;
60715
+ pcbPortsBySourcePortId = new Map;
60716
+ pcbTracesBySourceTraceId = new Map;
60717
+ pcbTracesByConnectionName = new Map;
60718
+ tracesById = new Map;
60719
+ tracesByPortId = new Map;
60720
+ tracesByNetId = new Map;
60721
+ constructor(circuitJson) {
60722
+ for (const item of circuitJson) {
60723
+ switch (item.type) {
60724
+ case "source_component":
60725
+ this.addComponent(item);
60726
+ break;
60727
+ case "source_port":
60728
+ this.addPort(item);
60729
+ break;
60730
+ case "source_net":
60731
+ this.addNet(item);
60732
+ break;
60733
+ case "pcb_port":
60734
+ this.addPcbPort(item);
60735
+ break;
60736
+ case "pcb_trace":
60737
+ this.addPcbTrace(item);
60738
+ break;
60739
+ case "source_trace":
60740
+ this.addTrace(item);
60741
+ break;
60742
+ default:
60743
+ break;
60744
+ }
60745
+ }
60746
+ }
60747
+ getComponentByName(name) {
60748
+ return this.lookupWithCaseFallback(this.componentsByName, name);
60749
+ }
60750
+ getNetByName(name) {
60751
+ return this.lookupWithCaseFallback(this.netsByName, name);
60752
+ }
60753
+ getPcbPort(sourcePortId) {
60754
+ return this.pcbPortsBySourcePortId.get(sourcePortId)?.[0];
60755
+ }
60756
+ getPortPosition(sourcePortId) {
60757
+ const pcbPort = this.getPcbPort(sourcePortId);
60758
+ if (!pcbPort || typeof pcbPort.x !== "number" || typeof pcbPort.y !== "number") {
60759
+ return null;
60760
+ }
60761
+ const layers = normalizeLayers(pcbPort.layers);
60762
+ return {
60763
+ x: pcbPort.x,
60764
+ y: pcbPort.y,
60765
+ layers
60766
+ };
60767
+ }
60768
+ getPortReference(sourcePortId) {
60769
+ const port = this.portsById.get(sourcePortId);
60770
+ if (!port) {
60771
+ return sourcePortId;
60772
+ }
60773
+ const component = this.componentsById.get(port.source_component_id);
60774
+ const componentName = component?.name ?? port.source_component_id;
60775
+ const portName = port.name ?? String(port.pin_number ?? port.source_port_id);
60776
+ return `${componentName}.${portName}`;
60777
+ }
60778
+ getPcbPathPoints(sourceTraceId) {
60779
+ const candidates = dedupePcbTraces([
60780
+ ...this.pcbTracesBySourceTraceId.get(sourceTraceId) ?? [],
60781
+ ...this.pcbTracesByConnectionName.get(sourceTraceId) ?? []
60782
+ ]);
60783
+ const pcbTraceIds = new Set(candidates.map((trace) => trace.pcb_trace_id));
60784
+ if (candidates.length === 0 || pcbTraceIds.size > 1) {
60785
+ return [];
60786
+ }
60787
+ const routePoints = candidates.flatMap((trace) => normalizePcbTraceRoute(trace));
60788
+ return convertPcbTraceRouteToPath(routePoints);
60789
+ }
60790
+ getConnectedPinsForNet(sourceNetId) {
60791
+ const traces = this.tracesByNetId.get(sourceNetId) ?? [];
60792
+ const sourcePortIds = new Set;
60793
+ for (const trace of traces) {
60794
+ for (const sourcePortId of trace.connected_source_port_ids ?? []) {
60795
+ sourcePortIds.add(sourcePortId);
60796
+ }
60797
+ }
60798
+ return [...sourcePortIds].map((sourcePortId) => this.toConnectedPin(sourcePortId));
60799
+ }
60800
+ toConnectedPin(sourcePortId) {
60801
+ const position2 = this.getPortPosition(sourcePortId);
60802
+ return {
60803
+ ref: this.getPortReference(sourcePortId),
60804
+ x: position2?.x ?? null,
60805
+ y: position2?.y ?? null,
60806
+ layers: position2?.layers ?? []
60807
+ };
60808
+ }
60809
+ addComponent(component) {
60810
+ this.componentsById.set(component.source_component_id, component);
60811
+ const name = component.name;
60812
+ if (!name) {
60813
+ return;
60814
+ }
60815
+ this.addLookup(this.componentsByName, name, component);
60816
+ }
60817
+ addPort(port) {
60818
+ this.portsById.set(port.source_port_id, port);
60819
+ this.addLookup(this.portsByComponentId, port.source_component_id, port);
60820
+ }
60821
+ addNet(net) {
60822
+ this.netsById.set(net.source_net_id, net);
60823
+ const name = net.name;
60824
+ if (!name) {
60825
+ return;
60826
+ }
60827
+ this.addLookup(this.netsByName, name, net);
60828
+ }
60829
+ addPcbPort(pcbPort) {
60830
+ this.addLookup(this.pcbPortsBySourcePortId, pcbPort.source_port_id, pcbPort);
60831
+ }
60832
+ addPcbTrace(pcbTrace) {
60833
+ if (pcbTrace.source_trace_id) {
60834
+ this.addLookup(this.pcbTracesBySourceTraceId, pcbTrace.source_trace_id, pcbTrace);
60835
+ }
60836
+ if (pcbTrace.connection_name) {
60837
+ this.addLookup(this.pcbTracesByConnectionName, pcbTrace.connection_name, pcbTrace);
60838
+ }
60839
+ }
60840
+ addTrace(trace) {
60841
+ this.tracesById.set(trace.source_trace_id, trace);
60842
+ for (const sourcePortId of trace.connected_source_port_ids ?? []) {
60843
+ this.addLookup(this.tracesByPortId, sourcePortId, trace);
60844
+ }
60845
+ for (const sourceNetId of trace.connected_source_net_ids ?? []) {
60846
+ this.addLookup(this.tracesByNetId, sourceNetId, trace);
60847
+ }
60848
+ }
60849
+ addLookup(map, key, value) {
60850
+ const existing = map.get(key) ?? [];
60851
+ existing.push(value);
60852
+ map.set(key, existing);
60853
+ }
60854
+ lookupWithCaseFallback(map, key) {
60855
+ const exactMatch = map.get(key);
60856
+ if (exactMatch?.length) {
60857
+ return exactMatch;
60858
+ }
60859
+ const loweredKey = key.toLowerCase();
60860
+ const fuzzyMatches = [];
60861
+ for (const [candidateKey, values] of map.entries()) {
60862
+ if (candidateKey.toLowerCase() === loweredKey) {
60863
+ fuzzyMatches.push(...values);
60864
+ }
60865
+ }
60866
+ return fuzzyMatches;
60867
+ }
60868
+ }
60869
+ function resolveTarget(index, targetPinOrNet) {
60870
+ if (targetPinOrNet.includes(".")) {
60871
+ if (targetPinOrNet.startsWith("net.")) {
60872
+ const netName = targetPinOrNet.slice("net.".length);
60873
+ const net = resolveNetByName(index, netName);
60874
+ return {
60875
+ kind: "net",
60876
+ requestedTarget: targetPinOrNet,
60877
+ resolvedTarget: `net.${net.name ?? net.source_net_id}`,
60878
+ net
60879
+ };
60880
+ }
60881
+ return resolvePinTarget(index, targetPinOrNet);
60882
+ }
60883
+ const matchingNet = resolveOptionalNetByName(index, targetPinOrNet);
60884
+ if (matchingNet) {
60885
+ const resolvedTarget = `net.${matchingNet.name ?? matchingNet.source_net_id}`;
60886
+ console.log(`inferring ${resolvedTarget}`);
60887
+ return {
60888
+ kind: "net",
60889
+ requestedTarget: targetPinOrNet,
60890
+ resolvedTarget,
60891
+ net: matchingNet
60892
+ };
60893
+ }
60894
+ const matchingPin = resolveOptionalPinByLabel(index, targetPinOrNet);
60895
+ if (matchingPin) {
60896
+ const resolvedTarget = index.getPortReference(matchingPin.source_port_id);
60897
+ console.log(`inferring ${resolvedTarget}`);
60898
+ return {
60899
+ kind: "pin",
60900
+ requestedTarget: targetPinOrNet,
60901
+ resolvedTarget,
60902
+ port: matchingPin
60903
+ };
60904
+ }
60905
+ throw new Error(`Unable to resolve target "${targetPinOrNet}" to a pin or net`);
60906
+ }
60907
+ function resolvePinTarget(index, targetPinOrNet) {
60908
+ const [componentName, ...pinParts] = targetPinOrNet.split(".");
60909
+ const pinLabelOrNumber = pinParts.join(".");
60910
+ if (!componentName || !pinLabelOrNumber) {
60911
+ throw new Error(`Invalid pin target "${targetPinOrNet}"`);
60912
+ }
60913
+ const components = index.getComponentByName(componentName);
60914
+ if (components.length !== 1) {
60915
+ throw new Error(components.length === 0 ? `Unable to find component "${componentName}"` : `Component "${componentName}" is ambiguous`);
60916
+ }
60917
+ const component = components[0];
60918
+ if (!component) {
60919
+ throw new Error(`Unable to find component "${componentName}"`);
60920
+ }
60921
+ const ports = index.portsByComponentId.get(component.source_component_id) ?? [];
60922
+ const matchingPorts = ports.filter((port2) => portMatchesLabel(port2, pinLabelOrNumber));
60923
+ if (matchingPorts.length !== 1) {
60924
+ throw new Error(matchingPorts.length === 0 ? `Unable to find pin "${pinLabelOrNumber}" on ${componentName}` : `Pin "${pinLabelOrNumber}" on ${componentName} is ambiguous`);
60925
+ }
60926
+ const port = matchingPorts[0];
60927
+ if (!port) {
60928
+ throw new Error(`Unable to find pin "${pinLabelOrNumber}" on ${componentName}`);
60929
+ }
60930
+ return {
60931
+ kind: "pin",
60932
+ requestedTarget: targetPinOrNet,
60933
+ resolvedTarget: index.getPortReference(port.source_port_id),
60934
+ port
60935
+ };
60936
+ }
60937
+ function resolveOptionalPinByLabel(index, pinLabelOrNumber) {
60938
+ const matchingPorts = [...index.portsById.values()].filter((port) => portMatchesLabel(port, pinLabelOrNumber));
60939
+ if (matchingPorts.length !== 1) {
60940
+ return null;
60941
+ }
60942
+ return matchingPorts[0] ?? null;
60943
+ }
60944
+ function resolveNetByName(index, netName) {
60945
+ const matchingNets = index.getNetByName(netName);
60946
+ if (matchingNets.length !== 1) {
60947
+ throw new Error(matchingNets.length === 0 ? `Unable to find net "${netName}"` : `Net "${netName}" is ambiguous`);
60948
+ }
60949
+ const net = matchingNets[0];
60950
+ if (!net) {
60951
+ throw new Error(`Unable to find net "${netName}"`);
60952
+ }
60953
+ return net;
60954
+ }
60955
+ function resolveOptionalNetByName(index, netName) {
60956
+ const matchingNets = index.getNetByName(netName);
60957
+ if (matchingNets.length !== 1) {
60958
+ return null;
60959
+ }
60960
+ return matchingNets[0] ?? null;
60961
+ }
60962
+ function collectPinTraces(index, target) {
60963
+ const traces = index.tracesByPortId.get(target.port.source_port_id) ?? [];
60964
+ return traces.flatMap((trace) => {
60965
+ const netIds = trace.connected_source_net_ids ?? [];
60966
+ if (netIds.length > 0) {
60967
+ return netIds.map((sourceNetId) => createNetTraceModel(index, {
60968
+ trace,
60969
+ sourceNetId,
60970
+ focusSourcePortId: target.port.source_port_id
60971
+ }));
60972
+ }
60973
+ return [
60974
+ createDirectTraceModel(index, {
60975
+ trace,
60976
+ focusSourcePortId: target.port.source_port_id
60977
+ })
60978
+ ];
60979
+ }).sort(compareTraceModels).map((model) => new Trace(model));
60980
+ }
60981
+ function collectNetTraces(index, target) {
60982
+ const traces = index.tracesByNetId.get(target.net.source_net_id) ?? [];
60983
+ return traces.flatMap((trace) => {
60984
+ const connectedSourcePortIds = trace.connected_source_port_ids ?? [];
60985
+ if (connectedSourcePortIds.length === 0) {
60986
+ return [];
60987
+ }
60988
+ return connectedSourcePortIds.map((focusSourcePortId) => createNetTraceModel(index, {
60989
+ trace,
60990
+ sourceNetId: target.net.source_net_id,
60991
+ focusSourcePortId
60992
+ }));
60993
+ }).sort(compareTraceModels).map((model) => new Trace(model));
60994
+ }
60995
+ function createDirectTraceModel(index, args) {
60996
+ const connectedSourcePortIds = args.trace.connected_source_port_ids ?? [];
60997
+ if (connectedSourcePortIds.length === 0) {
60998
+ throw new Error(`Trace ${args.trace.source_trace_id} has no connected ports`);
60999
+ }
61000
+ const orderedPortIds = prioritizeFocus(connectedSourcePortIds, args.focusSourcePortId);
61001
+ const firstPortId = orderedPortIds[0];
61002
+ const secondPortId = orderedPortIds[1] ?? orderedPortIds[0];
61003
+ if (!firstPortId || !secondPortId) {
61004
+ throw new Error(`Trace ${args.trace.source_trace_id} is missing connected ports`);
61005
+ }
61006
+ const firstPosition = requirePortPosition(index, firstPortId);
61007
+ const secondPosition = requirePortPosition(index, secondPortId);
61008
+ const secondLayer = primaryLayer(secondPosition.layers);
61009
+ const connectedPins = orderedPortIds.map((sourcePortId) => index.toConnectedPin(sourcePortId));
61010
+ const connectionTarget4 = index.getPortReference(secondPortId);
61011
+ const straightLineDistanceMm = distanceBetween(firstPosition, secondPosition);
61012
+ const points = index.getPcbPathPoints(args.trace.source_trace_id);
61013
+ const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
61014
+ return {
61015
+ id: args.trace.source_trace_id,
61016
+ label: `${index.getPortReference(firstPortId)} -> ${connectionTarget4}`,
61017
+ connectionType: "direct connection",
61018
+ connectionTarget: connectionTarget4,
61019
+ connectionTargetPosition: {
61020
+ x: secondPosition.x,
61021
+ y: secondPosition.y,
61022
+ layer: secondLayer
61023
+ },
61024
+ connectedPins,
61025
+ pinPositions: connectedPins,
61026
+ requirements: {
61027
+ maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
61028
+ },
61029
+ points,
61030
+ lengthMm,
61031
+ straightLineDistanceMm,
61032
+ sourceTraceId: args.trace.source_trace_id,
61033
+ displayName: args.trace.display_name ?? null
61034
+ };
61035
+ }
61036
+ function createNetTraceModel(index, args) {
61037
+ const net = index.netsById.get(args.sourceNetId);
61038
+ if (!net) {
61039
+ throw new Error(`Unable to find net ${args.sourceNetId}`);
61040
+ }
61041
+ const focusPosition = requirePortPosition(index, args.focusSourcePortId);
61042
+ const focusLayer = primaryLayer(focusPosition.layers);
61043
+ const connectedPins = index.getConnectedPinsForNet(args.sourceNetId).sort((a2, b) => a2.ref.localeCompare(b.ref));
61044
+ const hub = inferNetHub(connectedPins, focusLayer);
61045
+ const focusPin = index.toConnectedPin(args.focusSourcePortId);
61046
+ const points = index.getPcbPathPoints(args.trace.source_trace_id);
61047
+ const straightLineDistanceMm = distanceBetween(focusPosition, hub);
61048
+ const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
61049
+ return {
61050
+ id: `${args.trace.source_trace_id}:${args.focusSourcePortId}`,
61051
+ label: `${focusPin.ref} -> net.${net.name ?? net.source_net_id}`,
61052
+ connectionType: "via net",
61053
+ connectionTarget: `net.${net.name ?? net.source_net_id}`,
61054
+ connectionTargetPosition: hub,
61055
+ connectedPins: prioritizePin(focusPin, connectedPins),
61056
+ pinPositions: prioritizePin(focusPin, connectedPins),
61057
+ requirements: {
61058
+ maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
61059
+ },
61060
+ points,
61061
+ lengthMm,
61062
+ straightLineDistanceMm,
61063
+ sourceTraceId: args.trace.source_trace_id,
61064
+ displayName: args.trace.display_name ?? null
61065
+ };
61066
+ }
61067
+ function requirePortPosition(index, sourcePortId) {
61068
+ const position2 = index.getPortPosition(sourcePortId);
61069
+ if (!position2) {
61070
+ throw new Error(`Unable to analyze ${index.getPortReference(sourcePortId)} because it has no pcb_port position`);
61071
+ }
61072
+ return position2;
61073
+ }
61074
+ function inferNetHub(connectedPins, fallbackLayer) {
61075
+ const positionedPins = connectedPins.filter((pin) => typeof pin.x === "number" && typeof pin.y === "number");
61076
+ if (positionedPins.length === 0) {
61077
+ return {
61078
+ x: 0,
61079
+ y: 0,
61080
+ layer: fallbackLayer
61081
+ };
61082
+ }
61083
+ const x3 = positionedPins.reduce((sum, pin) => sum + pin.x, 0) / positionedPins.length;
61084
+ const y4 = positionedPins.reduce((sum, pin) => sum + pin.y, 0) / positionedPins.length;
61085
+ const layerCounts = new Map;
61086
+ for (const pin of positionedPins) {
61087
+ const layer2 = pin.layers[0] ?? fallbackLayer;
61088
+ layerCounts.set(layer2, (layerCounts.get(layer2) ?? 0) + 1);
61089
+ }
61090
+ const sortedLayers = [...layerCounts.entries()].sort((left, right) => {
61091
+ if (right[1] !== left[1]) {
61092
+ return right[1] - left[1];
61093
+ }
61094
+ return left[0].localeCompare(right[0]);
61095
+ });
61096
+ const layer = sortedLayers[0]?.[0] ?? fallbackLayer;
61097
+ return { x: x3, y: y4, layer };
61098
+ }
61099
+ function measurePathLength(points) {
61100
+ let lengthMm = 0;
61101
+ for (let index = 1;index < points.length; index += 1) {
61102
+ const previous = points[index - 1];
61103
+ const current2 = points[index];
61104
+ if (!previous || !current2) {
61105
+ continue;
61106
+ }
61107
+ lengthMm += distanceBetween(previous, current2);
61108
+ }
61109
+ return lengthMm;
61110
+ }
61111
+ function dedupePcbTraces(pcbTraces) {
61112
+ const seen = new Set;
61113
+ const deduped = [];
61114
+ for (const pcbTrace of pcbTraces) {
61115
+ if (seen.has(pcbTrace)) {
61116
+ continue;
61117
+ }
61118
+ seen.add(pcbTrace);
61119
+ deduped.push(pcbTrace);
61120
+ }
61121
+ return deduped;
61122
+ }
61123
+ function normalizePcbTraceRoute(pcbTrace) {
61124
+ if (!Array.isArray(pcbTrace.route)) {
61125
+ return [];
61126
+ }
61127
+ return pcbTrace.route.filter((point5) => typeof point5.x === "number" && typeof point5.y === "number").map((point5) => ({
61128
+ ...point5,
61129
+ layer: typeof point5.layer === "string" && point5.layer.length > 0 ? point5.layer : typeof pcbTrace.layer === "string" && pcbTrace.layer.length > 0 ? pcbTrace.layer : "top"
61130
+ }));
61131
+ }
61132
+ function convertPcbTraceRouteToPath(routePoints) {
61133
+ const deduped = routePoints.filter((point5, index) => {
61134
+ const previous = routePoints[index - 1];
61135
+ return !previous || previous.x !== point5.x || previous.y !== point5.y || previous.layer !== point5.layer;
61136
+ });
61137
+ return deduped.map((point5, index) => ({
61138
+ x: point5.x,
61139
+ y: point5.y,
61140
+ layer: point5.layer,
61141
+ kind: classifyPcbTracePoint(point5, index, deduped.length)
61142
+ }));
61143
+ }
61144
+ function classifyPcbTracePoint(point5, index, pointCount) {
61145
+ if (point5.route_type === "via") {
61146
+ return "via";
61147
+ }
61148
+ if (index === 0 || index === pointCount - 1 || point5.start_pcb_port_id || point5.end_pcb_port_id) {
61149
+ return "endpoint";
61150
+ }
61151
+ return "track";
61152
+ }
61153
+ function renderPinPosition(pin, indent) {
61154
+ if (typeof pin.x !== "number" || typeof pin.y !== "number") {
61155
+ return `${indent}<Pin ref="${escapeXml(pin.ref)}" position="unavailable" />`;
61156
+ }
61157
+ const layers = pin.layers.length > 0 ? pin.layers.join(",") : "unknown";
61158
+ return `${indent}<Pin ref="${escapeXml(pin.ref)}" x="${formatNumber3(pin.x)}" y="${formatNumber3(pin.y)}" layers="${escapeXml(layers)}" />`;
61159
+ }
61160
+ function renderConnection(trace) {
61161
+ if (trace.connectionTargetPosition) {
61162
+ return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" x="${formatNumber3(trace.connectionTargetPosition.x)}" y="${formatNumber3(trace.connectionTargetPosition.y)}" layer="${escapeXml(trace.connectionTargetPosition.layer)}" />`;
61163
+ }
61164
+ return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" />`;
61165
+ }
61166
+ function renderRequirements(requirements, indent) {
61167
+ if (typeof requirements.maxLengthMm === "number") {
61168
+ return [
61169
+ `${indent}<TraceRequirements>`,
61170
+ `${indent} <MaxLengthMm>${formatNumber3(requirements.maxLengthMm)}</MaxLengthMm>`,
61171
+ `${indent}</TraceRequirements>`
61172
+ ].join(`
61173
+ `);
61174
+ }
61175
+ return `${indent}<TraceRequirements none />`;
61176
+ }
61177
+ function portMatchesLabel(port, label) {
61178
+ const loweredLabel = label.toLowerCase();
61179
+ const portName = port.name?.toLowerCase();
61180
+ const pinNumber = String(port.pin_number ?? "").toLowerCase();
61181
+ const portHints2 = (port.port_hints ?? []).map((hint) => hint.toLowerCase());
61182
+ return portName === loweredLabel || pinNumber === loweredLabel || portHints2.includes(loweredLabel);
61183
+ }
61184
+ function prioritizeFocus(portIds, focusSourcePortId) {
61185
+ const remainder = portIds.filter((portId) => portId !== focusSourcePortId);
61186
+ return [focusSourcePortId, ...remainder];
61187
+ }
61188
+ function prioritizePin(focusPin, pins) {
61189
+ const remainder = pins.filter((pin) => pin.ref !== focusPin.ref);
61190
+ return [focusPin, ...remainder];
61191
+ }
61192
+ function compareTraceModels(left, right) {
61193
+ return left.label.localeCompare(right.label);
61194
+ }
61195
+ function normalizeLayers(layers) {
61196
+ if (!Array.isArray(layers)) {
61197
+ return ["top"];
61198
+ }
61199
+ const normalizedLayers = layers.filter((layer) => typeof layer === "string" && layer.length > 0);
61200
+ return normalizedLayers.length > 0 ? normalizedLayers : ["top"];
61201
+ }
61202
+ function primaryLayer(layers) {
61203
+ return layers[0] ?? "top";
61204
+ }
61205
+ function distanceBetween(left, right) {
61206
+ return Math.hypot(right.x - left.x, right.y - left.y);
61207
+ }
61208
+ function formatNumber3(value) {
61209
+ return value.toFixed(2);
61210
+ }
61211
+ function escapeXml(value) {
61212
+ return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
61213
+ }
61214
+
61215
+ // node_modules/circuit-json-trace-length-analysis/lib/index.ts
61216
+ var exports_lib = {};
61217
+ __export2(exports_lib, {
61218
+ analyzeCircuitJsonTraceLength: () => analyzeCircuitJsonTraceLength,
61219
+ TraceLengthAnalysis: () => TraceLengthAnalysis,
61220
+ Trace: () => Trace
61221
+ });
61222
+ var init_lib = () => {};
61223
+
60614
61224
  // node_modules/@edge-runtime/node-utils/dist/index.js
60615
61225
  var require_dist5 = __commonJS((exports2, module2) => {
60616
61226
  var __filename = "/home/runner/work/cli/cli/node_modules/@edge-runtime/node-utils/dist/index.js";
@@ -99964,7 +100574,7 @@ var import_perfect_cli = __toESM2(require_dist2(), 1);
99964
100574
  // lib/getVersion.ts
99965
100575
  import { createRequire as createRequire2 } from "node:module";
99966
100576
  // package.json
99967
- var version = "0.1.1289";
100577
+ var version = "0.1.1290";
99968
100578
  var package_default = {
99969
100579
  name: "@tscircuit/cli",
99970
100580
  version,
@@ -109909,7 +110519,10 @@ var buildPreviewGltf = async ({
109909
110519
  // cli/build/build-preview-images.ts
109910
110520
  import fs30 from "node:fs";
109911
110521
  import path32 from "node:path";
109912
- import { convertCircuitJsonToGltf as convertCircuitJsonToGltf3 } from "circuit-json-to-gltf";
110522
+ import {
110523
+ convertCircuitJsonToGltf as convertCircuitJsonToGltf3,
110524
+ getBestCameraPosition
110525
+ } from "circuit-json-to-gltf";
109913
110526
  import {
109914
110527
  convertCircuitJsonToPcbSvg,
109915
110528
  convertCircuitJsonToSchematicSvg
@@ -110016,7 +110629,7 @@ var generatePreviewAssets = async ({
110016
110629
  const glbBuffer = await convertCircuitJsonToGltf3(circuitJsonWithFileUrls, getCircuitJsonToGltfOptions({ format: "glb" }));
110017
110630
  console.log(`${prefix}Rendering GLB to PNG buffer...`);
110018
110631
  const glbArrayBuffer = await normalizeToArrayBuffer(glbBuffer);
110019
- const pngBuffer = await renderGLTFToPNGBufferFromGLBBuffer(glbArrayBuffer);
110632
+ const pngBuffer = await renderGLTFToPNGBufferFromGLBBuffer(glbArrayBuffer, getBestCameraPosition(circuitJson));
110020
110633
  fs30.writeFileSync(path32.join(outputDir, "3d.png"), Buffer.from(normalizeToUint8Array2(pngBuffer)));
110021
110634
  console.log(`${prefix}Written 3d.png`);
110022
110635
  } catch (error) {
@@ -240875,607 +241488,11 @@ var registerCheckSchematicPlacement = (program2) => {
240875
241488
  });
240876
241489
  };
240877
241490
 
240878
- // node_modules/circuit-json-trace-length-analysis/lib/analyze-circuit-json-trace-length.ts
240879
- class Trace {
240880
- id;
240881
- label;
240882
- connectionType;
240883
- connectionTarget;
240884
- connectionTargetPosition;
240885
- connectedPins;
240886
- pinPositions;
240887
- requirements;
240888
- points;
240889
- lengthMm;
240890
- straightLineDistanceMm;
240891
- sourceTraceId;
240892
- displayName;
240893
- constructor(model) {
240894
- this.id = model.id;
240895
- this.label = model.label;
240896
- this.connectionType = model.connectionType;
240897
- this.connectionTarget = model.connectionTarget;
240898
- this.connectionTargetPosition = model.connectionTargetPosition;
240899
- this.connectedPins = model.connectedPins;
240900
- this.pinPositions = model.pinPositions;
240901
- this.requirements = model.requirements;
240902
- this.points = model.points;
240903
- this.lengthMm = model.lengthMm;
240904
- this.straightLineDistanceMm = model.straightLineDistanceMm;
240905
- this.sourceTraceId = model.sourceTraceId;
240906
- this.displayName = model.displayName;
240907
- }
240908
- toString() {
240909
- const lines = [
240910
- `<Trace id="${escapeXml(this.id)}" label="${escapeXml(this.label)}" connectionType="${escapeXml(this.connectionType)}" lengthMm="${formatNumber3(this.lengthMm)}" straightLineDistanceMm="${formatNumber3(this.straightLineDistanceMm)}">`,
240911
- ` <ConnectedPins>`,
240912
- ...this.connectedPins.map((pin) => ` <Pin ref="${escapeXml(pin.ref)}" />`),
240913
- ` </ConnectedPins>`,
240914
- ` <PinPositions>`,
240915
- ...this.pinPositions.map((pin) => renderPinPosition(pin, " ")),
240916
- ` </PinPositions>`,
240917
- renderConnection(this),
240918
- renderRequirements(this.requirements, " "),
240919
- `</Trace>`
240920
- ];
240921
- if (this.points.length > 0) {
240922
- lines.splice(lines.length - 1, 0, ` <Path>`, ...this.points.map((point5) => ` <Point x="${formatNumber3(point5.x)}" y="${formatNumber3(point5.y)}" layer="${escapeXml(point5.layer)}" kind="${escapeXml(point5.kind)}" />`), ` </Path>`);
240923
- }
240924
- return lines.join(`
240925
- `);
240926
- }
240927
- }
240928
-
240929
- class TraceLengthAnalysis {
240930
- requestedTarget;
240931
- resolvedTarget;
240932
- targetKind;
240933
- totalLengthMm;
240934
- totalStraightLineDistanceMm;
240935
- traceCount;
240936
- #traces;
240937
- constructor(args) {
240938
- this.requestedTarget = args.requestedTarget;
240939
- this.resolvedTarget = args.resolvedTarget;
240940
- this.targetKind = args.targetKind;
240941
- this.#traces = [...args.traces];
240942
- this.totalLengthMm = this.#traces.reduce((sum, trace) => sum + trace.lengthMm, 0);
240943
- this.totalStraightLineDistanceMm = this.#traces.reduce((sum, trace) => sum + trace.straightLineDistanceMm, 0);
240944
- this.traceCount = this.#traces.length;
240945
- }
240946
- listTraces() {
240947
- return [...this.#traces];
240948
- }
240949
- toString() {
240950
- const lines = [
240951
- `<TraceLengthAnalysis requestedTarget="${escapeXml(this.requestedTarget)}" resolvedTarget="${escapeXml(this.resolvedTarget)}" targetKind="${escapeXml(this.targetKind)}" traceCount="${this.traceCount}" totalLengthMm="${formatNumber3(this.totalLengthMm)}" totalStraightLineDistanceMm="${formatNumber3(this.totalStraightLineDistanceMm)}">`,
240952
- ...this.#traces.flatMap((trace) => trace.toString().split(`
240953
- `).map((line2) => ` ${line2}`)),
240954
- `</TraceLengthAnalysis>`
240955
- ];
240956
- return lines.join(`
240957
- `);
240958
- }
240959
- }
240960
- function analyzeCircuitJsonTraceLength(circuitJson, options) {
240961
- const index = new CircuitJsonIndex(circuitJson);
240962
- const target = resolveTarget(index, options.targetPinOrNet);
240963
- const traces = target.kind === "pin" ? collectPinTraces(index, target) : collectNetTraces(index, target);
240964
- return new TraceLengthAnalysis({
240965
- requestedTarget: target.requestedTarget,
240966
- resolvedTarget: target.resolvedTarget,
240967
- targetKind: target.kind,
240968
- traces
240969
- });
240970
- }
240971
-
240972
- class CircuitJsonIndex {
240973
- componentsById = new Map;
240974
- componentsByName = new Map;
240975
- portsById = new Map;
240976
- portsByComponentId = new Map;
240977
- netsById = new Map;
240978
- netsByName = new Map;
240979
- pcbPortsBySourcePortId = new Map;
240980
- pcbTracesBySourceTraceId = new Map;
240981
- pcbTracesByConnectionName = new Map;
240982
- tracesById = new Map;
240983
- tracesByPortId = new Map;
240984
- tracesByNetId = new Map;
240985
- constructor(circuitJson) {
240986
- for (const item of circuitJson) {
240987
- switch (item.type) {
240988
- case "source_component":
240989
- this.addComponent(item);
240990
- break;
240991
- case "source_port":
240992
- this.addPort(item);
240993
- break;
240994
- case "source_net":
240995
- this.addNet(item);
240996
- break;
240997
- case "pcb_port":
240998
- this.addPcbPort(item);
240999
- break;
241000
- case "pcb_trace":
241001
- this.addPcbTrace(item);
241002
- break;
241003
- case "source_trace":
241004
- this.addTrace(item);
241005
- break;
241006
- default:
241007
- break;
241008
- }
241009
- }
241010
- }
241011
- getComponentByName(name) {
241012
- return this.lookupWithCaseFallback(this.componentsByName, name);
241013
- }
241014
- getNetByName(name) {
241015
- return this.lookupWithCaseFallback(this.netsByName, name);
241016
- }
241017
- getPcbPort(sourcePortId) {
241018
- return this.pcbPortsBySourcePortId.get(sourcePortId)?.[0];
241019
- }
241020
- getPortPosition(sourcePortId) {
241021
- const pcbPort = this.getPcbPort(sourcePortId);
241022
- if (!pcbPort || typeof pcbPort.x !== "number" || typeof pcbPort.y !== "number") {
241023
- return null;
241024
- }
241025
- const layers = normalizeLayers(pcbPort.layers);
241026
- return {
241027
- x: pcbPort.x,
241028
- y: pcbPort.y,
241029
- layers
241030
- };
241031
- }
241032
- getPortReference(sourcePortId) {
241033
- const port = this.portsById.get(sourcePortId);
241034
- if (!port) {
241035
- return sourcePortId;
241036
- }
241037
- const component = this.componentsById.get(port.source_component_id);
241038
- const componentName = component?.name ?? port.source_component_id;
241039
- const portName = port.name ?? String(port.pin_number ?? port.source_port_id);
241040
- return `${componentName}.${portName}`;
241041
- }
241042
- getPcbPathPoints(sourceTraceId) {
241043
- const candidates = dedupePcbTraces([
241044
- ...this.pcbTracesBySourceTraceId.get(sourceTraceId) ?? [],
241045
- ...this.pcbTracesByConnectionName.get(sourceTraceId) ?? []
241046
- ]);
241047
- const pcbTraceIds = new Set(candidates.map((trace) => trace.pcb_trace_id));
241048
- if (candidates.length === 0 || pcbTraceIds.size > 1) {
241049
- return [];
241050
- }
241051
- const routePoints = candidates.flatMap((trace) => normalizePcbTraceRoute(trace));
241052
- return convertPcbTraceRouteToPath(routePoints);
241053
- }
241054
- getConnectedPinsForNet(sourceNetId) {
241055
- const traces = this.tracesByNetId.get(sourceNetId) ?? [];
241056
- const sourcePortIds = new Set;
241057
- for (const trace of traces) {
241058
- for (const sourcePortId of trace.connected_source_port_ids ?? []) {
241059
- sourcePortIds.add(sourcePortId);
241060
- }
241061
- }
241062
- return [...sourcePortIds].map((sourcePortId) => this.toConnectedPin(sourcePortId));
241063
- }
241064
- toConnectedPin(sourcePortId) {
241065
- const position2 = this.getPortPosition(sourcePortId);
241066
- return {
241067
- ref: this.getPortReference(sourcePortId),
241068
- x: position2?.x ?? null,
241069
- y: position2?.y ?? null,
241070
- layers: position2?.layers ?? []
241071
- };
241072
- }
241073
- addComponent(component) {
241074
- this.componentsById.set(component.source_component_id, component);
241075
- const name = component.name;
241076
- if (!name) {
241077
- return;
241078
- }
241079
- this.addLookup(this.componentsByName, name, component);
241080
- }
241081
- addPort(port) {
241082
- this.portsById.set(port.source_port_id, port);
241083
- this.addLookup(this.portsByComponentId, port.source_component_id, port);
241084
- }
241085
- addNet(net) {
241086
- this.netsById.set(net.source_net_id, net);
241087
- const name = net.name;
241088
- if (!name) {
241089
- return;
241090
- }
241091
- this.addLookup(this.netsByName, name, net);
241092
- }
241093
- addPcbPort(pcbPort) {
241094
- this.addLookup(this.pcbPortsBySourcePortId, pcbPort.source_port_id, pcbPort);
241095
- }
241096
- addPcbTrace(pcbTrace) {
241097
- if (pcbTrace.source_trace_id) {
241098
- this.addLookup(this.pcbTracesBySourceTraceId, pcbTrace.source_trace_id, pcbTrace);
241099
- }
241100
- if (pcbTrace.connection_name) {
241101
- this.addLookup(this.pcbTracesByConnectionName, pcbTrace.connection_name, pcbTrace);
241102
- }
241103
- }
241104
- addTrace(trace) {
241105
- this.tracesById.set(trace.source_trace_id, trace);
241106
- for (const sourcePortId of trace.connected_source_port_ids ?? []) {
241107
- this.addLookup(this.tracesByPortId, sourcePortId, trace);
241108
- }
241109
- for (const sourceNetId of trace.connected_source_net_ids ?? []) {
241110
- this.addLookup(this.tracesByNetId, sourceNetId, trace);
241111
- }
241112
- }
241113
- addLookup(map, key, value) {
241114
- const existing = map.get(key) ?? [];
241115
- existing.push(value);
241116
- map.set(key, existing);
241117
- }
241118
- lookupWithCaseFallback(map, key) {
241119
- const exactMatch = map.get(key);
241120
- if (exactMatch?.length) {
241121
- return exactMatch;
241122
- }
241123
- const loweredKey = key.toLowerCase();
241124
- const fuzzyMatches = [];
241125
- for (const [candidateKey, values] of map.entries()) {
241126
- if (candidateKey.toLowerCase() === loweredKey) {
241127
- fuzzyMatches.push(...values);
241128
- }
241129
- }
241130
- return fuzzyMatches;
241131
- }
241132
- }
241133
- function resolveTarget(index, targetPinOrNet) {
241134
- if (targetPinOrNet.includes(".")) {
241135
- if (targetPinOrNet.startsWith("net.")) {
241136
- const netName = targetPinOrNet.slice("net.".length);
241137
- const net = resolveNetByName(index, netName);
241138
- return {
241139
- kind: "net",
241140
- requestedTarget: targetPinOrNet,
241141
- resolvedTarget: `net.${net.name ?? net.source_net_id}`,
241142
- net
241143
- };
241144
- }
241145
- return resolvePinTarget(index, targetPinOrNet);
241146
- }
241147
- const matchingNet = resolveOptionalNetByName(index, targetPinOrNet);
241148
- if (matchingNet) {
241149
- const resolvedTarget = `net.${matchingNet.name ?? matchingNet.source_net_id}`;
241150
- console.log(`inferring ${resolvedTarget}`);
241151
- return {
241152
- kind: "net",
241153
- requestedTarget: targetPinOrNet,
241154
- resolvedTarget,
241155
- net: matchingNet
241156
- };
241157
- }
241158
- const matchingPin = resolveOptionalPinByLabel(index, targetPinOrNet);
241159
- if (matchingPin) {
241160
- const resolvedTarget = index.getPortReference(matchingPin.source_port_id);
241161
- console.log(`inferring ${resolvedTarget}`);
241162
- return {
241163
- kind: "pin",
241164
- requestedTarget: targetPinOrNet,
241165
- resolvedTarget,
241166
- port: matchingPin
241167
- };
241168
- }
241169
- throw new Error(`Unable to resolve target "${targetPinOrNet}" to a pin or net`);
241170
- }
241171
- function resolvePinTarget(index, targetPinOrNet) {
241172
- const [componentName, ...pinParts] = targetPinOrNet.split(".");
241173
- const pinLabelOrNumber = pinParts.join(".");
241174
- if (!componentName || !pinLabelOrNumber) {
241175
- throw new Error(`Invalid pin target "${targetPinOrNet}"`);
241176
- }
241177
- const components = index.getComponentByName(componentName);
241178
- if (components.length !== 1) {
241179
- throw new Error(components.length === 0 ? `Unable to find component "${componentName}"` : `Component "${componentName}" is ambiguous`);
241180
- }
241181
- const component = components[0];
241182
- if (!component) {
241183
- throw new Error(`Unable to find component "${componentName}"`);
241184
- }
241185
- const ports = index.portsByComponentId.get(component.source_component_id) ?? [];
241186
- const matchingPorts = ports.filter((port2) => portMatchesLabel(port2, pinLabelOrNumber));
241187
- if (matchingPorts.length !== 1) {
241188
- throw new Error(matchingPorts.length === 0 ? `Unable to find pin "${pinLabelOrNumber}" on ${componentName}` : `Pin "${pinLabelOrNumber}" on ${componentName} is ambiguous`);
241189
- }
241190
- const port = matchingPorts[0];
241191
- if (!port) {
241192
- throw new Error(`Unable to find pin "${pinLabelOrNumber}" on ${componentName}`);
241193
- }
241194
- return {
241195
- kind: "pin",
241196
- requestedTarget: targetPinOrNet,
241197
- resolvedTarget: index.getPortReference(port.source_port_id),
241198
- port
241199
- };
241200
- }
241201
- function resolveOptionalPinByLabel(index, pinLabelOrNumber) {
241202
- const matchingPorts = [...index.portsById.values()].filter((port) => portMatchesLabel(port, pinLabelOrNumber));
241203
- if (matchingPorts.length !== 1) {
241204
- return null;
241205
- }
241206
- return matchingPorts[0] ?? null;
241207
- }
241208
- function resolveNetByName(index, netName) {
241209
- const matchingNets = index.getNetByName(netName);
241210
- if (matchingNets.length !== 1) {
241211
- throw new Error(matchingNets.length === 0 ? `Unable to find net "${netName}"` : `Net "${netName}" is ambiguous`);
241212
- }
241213
- const net = matchingNets[0];
241214
- if (!net) {
241215
- throw new Error(`Unable to find net "${netName}"`);
241216
- }
241217
- return net;
241218
- }
241219
- function resolveOptionalNetByName(index, netName) {
241220
- const matchingNets = index.getNetByName(netName);
241221
- if (matchingNets.length !== 1) {
241222
- return null;
241223
- }
241224
- return matchingNets[0] ?? null;
241225
- }
241226
- function collectPinTraces(index, target) {
241227
- const traces = index.tracesByPortId.get(target.port.source_port_id) ?? [];
241228
- return traces.flatMap((trace) => {
241229
- const netIds = trace.connected_source_net_ids ?? [];
241230
- if (netIds.length > 0) {
241231
- return netIds.map((sourceNetId) => createNetTraceModel(index, {
241232
- trace,
241233
- sourceNetId,
241234
- focusSourcePortId: target.port.source_port_id
241235
- }));
241236
- }
241237
- return [
241238
- createDirectTraceModel(index, {
241239
- trace,
241240
- focusSourcePortId: target.port.source_port_id
241241
- })
241242
- ];
241243
- }).sort(compareTraceModels).map((model) => new Trace(model));
241244
- }
241245
- function collectNetTraces(index, target) {
241246
- const traces = index.tracesByNetId.get(target.net.source_net_id) ?? [];
241247
- return traces.flatMap((trace) => {
241248
- const connectedSourcePortIds = trace.connected_source_port_ids ?? [];
241249
- if (connectedSourcePortIds.length === 0) {
241250
- return [];
241251
- }
241252
- return connectedSourcePortIds.map((focusSourcePortId) => createNetTraceModel(index, {
241253
- trace,
241254
- sourceNetId: target.net.source_net_id,
241255
- focusSourcePortId
241256
- }));
241257
- }).sort(compareTraceModels).map((model) => new Trace(model));
241258
- }
241259
- function createDirectTraceModel(index, args) {
241260
- const connectedSourcePortIds = args.trace.connected_source_port_ids ?? [];
241261
- if (connectedSourcePortIds.length === 0) {
241262
- throw new Error(`Trace ${args.trace.source_trace_id} has no connected ports`);
241263
- }
241264
- const orderedPortIds = prioritizeFocus(connectedSourcePortIds, args.focusSourcePortId);
241265
- const firstPortId = orderedPortIds[0];
241266
- const secondPortId = orderedPortIds[1] ?? orderedPortIds[0];
241267
- if (!firstPortId || !secondPortId) {
241268
- throw new Error(`Trace ${args.trace.source_trace_id} is missing connected ports`);
241269
- }
241270
- const firstPosition = requirePortPosition(index, firstPortId);
241271
- const secondPosition = requirePortPosition(index, secondPortId);
241272
- const secondLayer = primaryLayer(secondPosition.layers);
241273
- const connectedPins = orderedPortIds.map((sourcePortId) => index.toConnectedPin(sourcePortId));
241274
- const connectionTarget4 = index.getPortReference(secondPortId);
241275
- const straightLineDistanceMm = distanceBetween(firstPosition, secondPosition);
241276
- const points = index.getPcbPathPoints(args.trace.source_trace_id);
241277
- const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
241278
- return {
241279
- id: args.trace.source_trace_id,
241280
- label: `${index.getPortReference(firstPortId)} -> ${connectionTarget4}`,
241281
- connectionType: "direct connection",
241282
- connectionTarget: connectionTarget4,
241283
- connectionTargetPosition: {
241284
- x: secondPosition.x,
241285
- y: secondPosition.y,
241286
- layer: secondLayer
241287
- },
241288
- connectedPins,
241289
- pinPositions: connectedPins,
241290
- requirements: {
241291
- maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
241292
- },
241293
- points,
241294
- lengthMm,
241295
- straightLineDistanceMm,
241296
- sourceTraceId: args.trace.source_trace_id,
241297
- displayName: args.trace.display_name ?? null
241298
- };
241299
- }
241300
- function createNetTraceModel(index, args) {
241301
- const net = index.netsById.get(args.sourceNetId);
241302
- if (!net) {
241303
- throw new Error(`Unable to find net ${args.sourceNetId}`);
241304
- }
241305
- const focusPosition = requirePortPosition(index, args.focusSourcePortId);
241306
- const focusLayer = primaryLayer(focusPosition.layers);
241307
- const connectedPins = index.getConnectedPinsForNet(args.sourceNetId).sort((a2, b) => a2.ref.localeCompare(b.ref));
241308
- const hub = inferNetHub(connectedPins, focusLayer);
241309
- const focusPin = index.toConnectedPin(args.focusSourcePortId);
241310
- const points = index.getPcbPathPoints(args.trace.source_trace_id);
241311
- const straightLineDistanceMm = distanceBetween(focusPosition, hub);
241312
- const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
241313
- return {
241314
- id: `${args.trace.source_trace_id}:${args.focusSourcePortId}`,
241315
- label: `${focusPin.ref} -> net.${net.name ?? net.source_net_id}`,
241316
- connectionType: "via net",
241317
- connectionTarget: `net.${net.name ?? net.source_net_id}`,
241318
- connectionTargetPosition: hub,
241319
- connectedPins: prioritizePin(focusPin, connectedPins),
241320
- pinPositions: prioritizePin(focusPin, connectedPins),
241321
- requirements: {
241322
- maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
241323
- },
241324
- points,
241325
- lengthMm,
241326
- straightLineDistanceMm,
241327
- sourceTraceId: args.trace.source_trace_id,
241328
- displayName: args.trace.display_name ?? null
241329
- };
241330
- }
241331
- function requirePortPosition(index, sourcePortId) {
241332
- const position2 = index.getPortPosition(sourcePortId);
241333
- if (!position2) {
241334
- throw new Error(`Unable to analyze ${index.getPortReference(sourcePortId)} because it has no pcb_port position`);
241335
- }
241336
- return position2;
241337
- }
241338
- function inferNetHub(connectedPins, fallbackLayer) {
241339
- const positionedPins = connectedPins.filter((pin) => typeof pin.x === "number" && typeof pin.y === "number");
241340
- if (positionedPins.length === 0) {
241341
- return {
241342
- x: 0,
241343
- y: 0,
241344
- layer: fallbackLayer
241345
- };
241346
- }
241347
- const x3 = positionedPins.reduce((sum, pin) => sum + pin.x, 0) / positionedPins.length;
241348
- const y4 = positionedPins.reduce((sum, pin) => sum + pin.y, 0) / positionedPins.length;
241349
- const layerCounts = new Map;
241350
- for (const pin of positionedPins) {
241351
- const layer2 = pin.layers[0] ?? fallbackLayer;
241352
- layerCounts.set(layer2, (layerCounts.get(layer2) ?? 0) + 1);
241353
- }
241354
- const sortedLayers = [...layerCounts.entries()].sort((left, right) => {
241355
- if (right[1] !== left[1]) {
241356
- return right[1] - left[1];
241357
- }
241358
- return left[0].localeCompare(right[0]);
241359
- });
241360
- const layer = sortedLayers[0]?.[0] ?? fallbackLayer;
241361
- return { x: x3, y: y4, layer };
241362
- }
241363
- function measurePathLength(points) {
241364
- let lengthMm = 0;
241365
- for (let index = 1;index < points.length; index += 1) {
241366
- const previous = points[index - 1];
241367
- const current2 = points[index];
241368
- if (!previous || !current2) {
241369
- continue;
241370
- }
241371
- lengthMm += distanceBetween(previous, current2);
241372
- }
241373
- return lengthMm;
241374
- }
241375
- function dedupePcbTraces(pcbTraces) {
241376
- const seen = new Set;
241377
- const deduped = [];
241378
- for (const pcbTrace of pcbTraces) {
241379
- if (seen.has(pcbTrace)) {
241380
- continue;
241381
- }
241382
- seen.add(pcbTrace);
241383
- deduped.push(pcbTrace);
241384
- }
241385
- return deduped;
241386
- }
241387
- function normalizePcbTraceRoute(pcbTrace) {
241388
- if (!Array.isArray(pcbTrace.route)) {
241389
- return [];
241390
- }
241391
- return pcbTrace.route.filter((point5) => typeof point5.x === "number" && typeof point5.y === "number").map((point5) => ({
241392
- ...point5,
241393
- layer: typeof point5.layer === "string" && point5.layer.length > 0 ? point5.layer : typeof pcbTrace.layer === "string" && pcbTrace.layer.length > 0 ? pcbTrace.layer : "top"
241394
- }));
241395
- }
241396
- function convertPcbTraceRouteToPath(routePoints) {
241397
- const deduped = routePoints.filter((point5, index) => {
241398
- const previous = routePoints[index - 1];
241399
- return !previous || previous.x !== point5.x || previous.y !== point5.y || previous.layer !== point5.layer;
241400
- });
241401
- return deduped.map((point5, index) => ({
241402
- x: point5.x,
241403
- y: point5.y,
241404
- layer: point5.layer,
241405
- kind: classifyPcbTracePoint(point5, index, deduped.length)
241406
- }));
241407
- }
241408
- function classifyPcbTracePoint(point5, index, pointCount) {
241409
- if (point5.route_type === "via") {
241410
- return "via";
241411
- }
241412
- if (index === 0 || index === pointCount - 1 || point5.start_pcb_port_id || point5.end_pcb_port_id) {
241413
- return "endpoint";
241414
- }
241415
- return "track";
241416
- }
241417
- function renderPinPosition(pin, indent) {
241418
- if (typeof pin.x !== "number" || typeof pin.y !== "number") {
241419
- return `${indent}<Pin ref="${escapeXml(pin.ref)}" position="unavailable" />`;
241420
- }
241421
- const layers = pin.layers.length > 0 ? pin.layers.join(",") : "unknown";
241422
- return `${indent}<Pin ref="${escapeXml(pin.ref)}" x="${formatNumber3(pin.x)}" y="${formatNumber3(pin.y)}" layers="${escapeXml(layers)}" />`;
241423
- }
241424
- function renderConnection(trace) {
241425
- if (trace.connectionTargetPosition) {
241426
- return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" x="${formatNumber3(trace.connectionTargetPosition.x)}" y="${formatNumber3(trace.connectionTargetPosition.y)}" layer="${escapeXml(trace.connectionTargetPosition.layer)}" />`;
241427
- }
241428
- return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" />`;
241429
- }
241430
- function renderRequirements(requirements, indent) {
241431
- if (typeof requirements.maxLengthMm === "number") {
241432
- return [
241433
- `${indent}<TraceRequirements>`,
241434
- `${indent} <MaxLengthMm>${formatNumber3(requirements.maxLengthMm)}</MaxLengthMm>`,
241435
- `${indent}</TraceRequirements>`
241436
- ].join(`
241437
- `);
241438
- }
241439
- return `${indent}<TraceRequirements none />`;
241440
- }
241441
- function portMatchesLabel(port, label) {
241442
- const loweredLabel = label.toLowerCase();
241443
- const portName = port.name?.toLowerCase();
241444
- const pinNumber = String(port.pin_number ?? "").toLowerCase();
241445
- const portHints2 = (port.port_hints ?? []).map((hint) => hint.toLowerCase());
241446
- return portName === loweredLabel || pinNumber === loweredLabel || portHints2.includes(loweredLabel);
241447
- }
241448
- function prioritizeFocus(portIds, focusSourcePortId) {
241449
- const remainder = portIds.filter((portId) => portId !== focusSourcePortId);
241450
- return [focusSourcePortId, ...remainder];
241451
- }
241452
- function prioritizePin(focusPin, pins) {
241453
- const remainder = pins.filter((pin) => pin.ref !== focusPin.ref);
241454
- return [focusPin, ...remainder];
241455
- }
241456
- function compareTraceModels(left, right) {
241457
- return left.label.localeCompare(right.label);
241458
- }
241459
- function normalizeLayers(layers) {
241460
- if (!Array.isArray(layers)) {
241461
- return ["top"];
241462
- }
241463
- const normalizedLayers = layers.filter((layer) => typeof layer === "string" && layer.length > 0);
241464
- return normalizedLayers.length > 0 ? normalizedLayers : ["top"];
241465
- }
241466
- function primaryLayer(layers) {
241467
- return layers[0] ?? "top";
241468
- }
241469
- function distanceBetween(left, right) {
241470
- return Math.hypot(right.x - left.x, right.y - left.y);
241471
- }
241472
- function formatNumber3(value) {
241473
- return value.toFixed(2);
241474
- }
241475
- function escapeXml(value) {
241476
- return value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
241477
- }
241478
241491
  // cli/check/trace-length/register.ts
241492
+ var loadTraceLengthAnalyzer = async () => {
241493
+ const mod = await Promise.resolve().then(() => (init_lib(), exports_lib));
241494
+ return mod.analyzeCircuitJsonTraceLength;
241495
+ };
241479
241496
  var checkTraceLength = async (pinOrNetRef, file) => {
241480
241497
  const resolvedInputFilePath = await resolveCheckInputFilePath(file);
241481
241498
  const circuitJson = await getCircuitJsonForCheck({
@@ -241486,7 +241503,8 @@ var checkTraceLength = async (pinOrNetRef, file) => {
241486
241503
  },
241487
241504
  allowPrebuiltCircuitJson: true
241488
241505
  });
241489
- const analysis = analyzeCircuitJsonTraceLength(circuitJson, {
241506
+ const analyzeCircuitJsonTraceLength2 = await loadTraceLengthAnalyzer();
241507
+ const analysis = analyzeCircuitJsonTraceLength2(circuitJson, {
241490
241508
  targetPinOrNet: pinOrNetRef
241491
241509
  });
241492
241510
  return analysis.toString();
@@ -272770,7 +272788,7 @@ import fs63 from "node:fs";
272770
272788
  import path66 from "node:path";
272771
272789
  import {
272772
272790
  convertCircuitJsonToGltf as convertCircuitJsonToGltf5,
272773
- getBestCameraPosition
272791
+ getBestCameraPosition as getBestCameraPosition2
272774
272792
  } from "circuit-json-to-gltf";
272775
272793
  import {
272776
272794
  convertCircuitJsonToPcbSvg as convertCircuitJsonToPcbSvg4,
@@ -275581,7 +275599,7 @@ var processSnapshotFile = async ({
275581
275599
  if (!(glbBuffer instanceof ArrayBuffer)) {
275582
275600
  throw new Error("Expected ArrayBuffer from convertCircuitJsonToGltf with glb format");
275583
275601
  }
275584
- let cameraOptions = getBestCameraPosition(circuitJson);
275602
+ let cameraOptions = getBestCameraPosition2(circuitJson);
275585
275603
  if (cameraPreset) {
275586
275604
  cameraOptions = applyCameraPreset(cameraPreset, cameraOptions);
275587
275605
  }
package/dist/lib/index.js CHANGED
@@ -65661,7 +65661,7 @@ var getNodeHandler = (winterSpec, { port, middleware = [] }) => {
65661
65661
  }));
65662
65662
  };
65663
65663
  // package.json
65664
- var version = "0.1.1289";
65664
+ var version = "0.1.1290";
65665
65665
  var package_default = {
65666
65666
  name: "@tscircuit/cli",
65667
65667
  version,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/cli",
3
- "version": "0.1.1290",
3
+ "version": "0.1.1291",
4
4
  "main": "dist/cli/main.js",
5
5
  "exports": {
6
6
  ".": "./dist/cli/main.js",