@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.
- package/dist/cli/build/build.worker.js +5 -2
- package/dist/cli/main.js +624 -606
- package/dist/lib/index.js +1 -1
- package/package.json +1 -1
|
@@ -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 {
|
|
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("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
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.
|
|
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 {
|
|
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("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
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
|
|
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 =
|
|
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.
|
|
65664
|
+
var version = "0.1.1290";
|
|
65665
65665
|
var package_default = {
|
|
65666
65666
|
name: "@tscircuit/cli",
|
|
65667
65667
|
version,
|