@tscircuit/cli 0.1.1209 → 0.1.1210
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/main.js +1450 -56
- package/dist/lib/index.js +8 -7
- package/package.json +8 -7
package/dist/cli/main.js
CHANGED
|
@@ -37959,7 +37959,7 @@ var init_chunk_SK323GHE = __esm(() => {
|
|
|
37959
37959
|
debug2.namespace = namespace;
|
|
37960
37960
|
debug2.useColors = createDebug.useColors();
|
|
37961
37961
|
debug2.color = createDebug.selectColor(namespace);
|
|
37962
|
-
debug2.extend =
|
|
37962
|
+
debug2.extend = extend2;
|
|
37963
37963
|
debug2.destroy = createDebug.destroy;
|
|
37964
37964
|
Object.defineProperty(debug2, "enabled", {
|
|
37965
37965
|
enumerable: true,
|
|
@@ -37983,7 +37983,7 @@ var init_chunk_SK323GHE = __esm(() => {
|
|
|
37983
37983
|
}
|
|
37984
37984
|
return debug2;
|
|
37985
37985
|
}
|
|
37986
|
-
function
|
|
37986
|
+
function extend2(namespace, delimiter) {
|
|
37987
37987
|
const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
|
|
37988
37988
|
newDebug.log = this.log;
|
|
37989
37989
|
return newDebug;
|
|
@@ -39002,7 +39002,7 @@ var init_chunk_SK323GHE = __esm(() => {
|
|
|
39002
39002
|
element: "element",
|
|
39003
39003
|
text: "text"
|
|
39004
39004
|
};
|
|
39005
|
-
var
|
|
39005
|
+
var createNode2 = function createNode22(params2) {
|
|
39006
39006
|
return Object.assign({
|
|
39007
39007
|
name: "",
|
|
39008
39008
|
type: NodeType.element,
|
|
@@ -39030,7 +39030,7 @@ var init_chunk_SK323GHE = __esm(() => {
|
|
|
39030
39030
|
current2 = rootNode;
|
|
39031
39031
|
current2.name = data.value;
|
|
39032
39032
|
} else {
|
|
39033
|
-
var node =
|
|
39033
|
+
var node = createNode2({
|
|
39034
39034
|
name: data.value,
|
|
39035
39035
|
parent: current2
|
|
39036
39036
|
});
|
|
@@ -39063,7 +39063,7 @@ var init_chunk_SK323GHE = __esm(() => {
|
|
|
39063
39063
|
break;
|
|
39064
39064
|
case Type.text:
|
|
39065
39065
|
if (current2) {
|
|
39066
|
-
current2.children.push(
|
|
39066
|
+
current2.children.push(createNode2({
|
|
39067
39067
|
type: NodeType.text,
|
|
39068
39068
|
value: data.value,
|
|
39069
39069
|
parent: options.parentNodes ? current2 : null
|
|
@@ -39082,7 +39082,7 @@ var init_chunk_SK323GHE = __esm(() => {
|
|
|
39082
39082
|
reader.reset = function() {
|
|
39083
39083
|
lexer = Lexer.create({ debug: options.debug });
|
|
39084
39084
|
lexer.on("data", handleLexerData);
|
|
39085
|
-
rootNode =
|
|
39085
|
+
rootNode = createNode2();
|
|
39086
39086
|
current2 = null;
|
|
39087
39087
|
attrName = "";
|
|
39088
39088
|
reader.parse = lexer.write;
|
|
@@ -95866,7 +95866,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
95866
95866
|
glob2.sync = globSync;
|
|
95867
95867
|
var GlobSync = glob2.GlobSync = globSync.GlobSync;
|
|
95868
95868
|
glob2.glob = glob2;
|
|
95869
|
-
function
|
|
95869
|
+
function extend2(origin, add) {
|
|
95870
95870
|
if (add === null || typeof add !== "object") {
|
|
95871
95871
|
return origin;
|
|
95872
95872
|
}
|
|
@@ -95878,7 +95878,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
|
|
|
95878
95878
|
return origin;
|
|
95879
95879
|
}
|
|
95880
95880
|
glob2.hasMagic = function(pattern, options_) {
|
|
95881
|
-
var options =
|
|
95881
|
+
var options = extend2({}, options_);
|
|
95882
95882
|
options.noprocess = true;
|
|
95883
95883
|
var g6 = new Glob(pattern, options);
|
|
95884
95884
|
var set = g6.minimatch.set;
|
|
@@ -99964,7 +99964,7 @@ var import_perfect_cli = __toESM2(require_dist2(), 1);
|
|
|
99964
99964
|
// lib/getVersion.ts
|
|
99965
99965
|
import { createRequire as createRequire2 } from "node:module";
|
|
99966
99966
|
// package.json
|
|
99967
|
-
var version = "0.1.
|
|
99967
|
+
var version = "0.1.1209";
|
|
99968
99968
|
var package_default = {
|
|
99969
99969
|
name: "@tscircuit/cli",
|
|
99970
99970
|
version,
|
|
@@ -99976,7 +99976,7 @@ var package_default = {
|
|
|
99976
99976
|
devDependencies: {
|
|
99977
99977
|
"@babel/standalone": "^7.26.9",
|
|
99978
99978
|
"@biomejs/biome": "^1.9.4",
|
|
99979
|
-
"@tscircuit/circuit-json-placement-analysis": "^0.0.
|
|
99979
|
+
"@tscircuit/circuit-json-placement-analysis": "^0.0.6",
|
|
99980
99980
|
"@tscircuit/circuit-json-routing-analysis": "^0.0.1",
|
|
99981
99981
|
"@tscircuit/fake-snippets": "^0.0.182",
|
|
99982
99982
|
"@tscircuit/file-server": "^0.0.32",
|
|
@@ -99996,13 +99996,15 @@ var package_default = {
|
|
|
99996
99996
|
"bun-match-svg": "^0.0.12",
|
|
99997
99997
|
chokidar: "4.0.1",
|
|
99998
99998
|
"circuit-json": "^0.0.403",
|
|
99999
|
-
"circuit-json-to-gerber": "^0.0.48",
|
|
100000
99999
|
"circuit-json-to-bom-csv": "^0.0.7",
|
|
100001
|
-
"circuit-json-to-
|
|
100000
|
+
"circuit-json-to-gerber": "^0.0.48",
|
|
100002
100001
|
"circuit-json-to-kicad": "^0.0.96",
|
|
100002
|
+
"circuit-json-to-pnp-csv": "^0.0.7",
|
|
100003
100003
|
"circuit-json-to-readable-netlist": "^0.0.15",
|
|
100004
100004
|
"circuit-json-to-spice": "^0.0.10",
|
|
100005
|
+
"circuit-json-to-step": "^0.0.20",
|
|
100005
100006
|
"circuit-json-to-tscircuit": "^0.0.9",
|
|
100007
|
+
"circuit-json-trace-length-analysis": "github:tscircuit/circuit-json-trace-length-analysis#2b44792a40df0ca83b6bfb6ac95ed5e35e7168b8",
|
|
100006
100008
|
commander: "^14.0.0",
|
|
100007
100009
|
conf: "^13.1.0",
|
|
100008
100010
|
configstore: "^7.0.0",
|
|
@@ -100030,13 +100032,12 @@ var package_default = {
|
|
|
100030
100032
|
prompts: "^2.4.2",
|
|
100031
100033
|
redaxios: "^0.5.1",
|
|
100032
100034
|
semver: "^7.6.3",
|
|
100035
|
+
stepts: "^0.0.3",
|
|
100033
100036
|
tempy: "^3.1.0",
|
|
100034
100037
|
tscircuit: "0.0.1590-libonly",
|
|
100035
100038
|
tsx: "^4.7.1",
|
|
100036
100039
|
"typed-ky": "^0.0.4",
|
|
100037
|
-
zod: "^3.23.8"
|
|
100038
|
-
"circuit-json-to-step": "^0.0.20",
|
|
100039
|
-
stepts: "^0.0.3"
|
|
100040
|
+
zod: "^3.23.8"
|
|
100040
100041
|
},
|
|
100041
100042
|
peerDependencies: {
|
|
100042
100043
|
tscircuit: "*"
|
|
@@ -111779,7 +111780,12 @@ var convertCircuitJsonToReadableNetlist = (circuitJson) => {
|
|
|
111779
111780
|
};
|
|
111780
111781
|
|
|
111781
111782
|
// cli/check/netlist/register.ts
|
|
111783
|
+
import {
|
|
111784
|
+
categorizeErrorOrWarning as categorizeErrorOrWarning2
|
|
111785
|
+
} from "@tscircuit/circuit-json-util";
|
|
111782
111786
|
import path40 from "node:path";
|
|
111787
|
+
var normalizeCategory2 = (category) => category === "netlist" || category === "pin_specification" || category === "placement" || category === "routing" ? category : "unknown";
|
|
111788
|
+
var isNetlistDiagnostic = (issue) => normalizeCategory2(categorizeErrorOrWarning2(issue)) === "netlist";
|
|
111783
111789
|
var resolveInputFilePath = async (file) => {
|
|
111784
111790
|
if (file) {
|
|
111785
111791
|
return path40.isAbsolute(file) ? file : path40.resolve(process.cwd(), file);
|
|
@@ -111805,13 +111811,21 @@ var checkNetlist = async (file) => {
|
|
|
111805
111811
|
});
|
|
111806
111812
|
const typedCircuitJson = circuitJson;
|
|
111807
111813
|
const diagnostics = analyzeCircuitJson(typedCircuitJson);
|
|
111814
|
+
const netlistErrors = diagnostics.errors.filter(isNetlistDiagnostic);
|
|
111815
|
+
const netlistWarnings = diagnostics.warnings.filter(isNetlistDiagnostic);
|
|
111808
111816
|
const readableNetlist = convertCircuitJsonToReadableNetlist(typedCircuitJson);
|
|
111809
111817
|
const diagnosticsLines = [
|
|
111810
|
-
`Errors: ${
|
|
111811
|
-
`Warnings: ${
|
|
111818
|
+
`Errors: ${netlistErrors.length}`,
|
|
111819
|
+
`Warnings: ${netlistWarnings.length}`
|
|
111812
111820
|
];
|
|
111813
|
-
if (
|
|
111814
|
-
diagnosticsLines.push(...
|
|
111821
|
+
if (netlistErrors.length > 0) {
|
|
111822
|
+
diagnosticsLines.push(...netlistErrors.map((err) => `- ${err.type}: ${err.message ?? ""}`));
|
|
111823
|
+
}
|
|
111824
|
+
if (netlistWarnings.length > 0) {
|
|
111825
|
+
diagnosticsLines.push(...netlistWarnings.map((warning) => {
|
|
111826
|
+
const issueType = warning.warning_type ?? warning.error_type ?? warning.type;
|
|
111827
|
+
return `- ${issueType}: ${warning.message ?? ""}`;
|
|
111828
|
+
}));
|
|
111815
111829
|
}
|
|
111816
111830
|
return `${diagnosticsLines.join(`
|
|
111817
111831
|
`)}
|
|
@@ -111914,6 +111928,454 @@ var registerCheckPinSpecification = (program2) => {
|
|
|
111914
111928
|
});
|
|
111915
111929
|
};
|
|
111916
111930
|
|
|
111931
|
+
// node_modules/@tscircuit/circuit-json-placement-analysis/dist/index.js
|
|
111932
|
+
import Flatbush from "flatbush";
|
|
111933
|
+
|
|
111934
|
+
// node_modules/quickselect/index.js
|
|
111935
|
+
function quickselect(arr, k, left = 0, right = arr.length - 1, compare = defaultCompare) {
|
|
111936
|
+
while (right > left) {
|
|
111937
|
+
if (right - left > 600) {
|
|
111938
|
+
const n = right - left + 1;
|
|
111939
|
+
const m = k - left + 1;
|
|
111940
|
+
const z2 = Math.log(n);
|
|
111941
|
+
const s = 0.5 * Math.exp(2 * z2 / 3);
|
|
111942
|
+
const sd = 0.5 * Math.sqrt(z2 * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
|
|
111943
|
+
const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
|
|
111944
|
+
const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
|
|
111945
|
+
quickselect(arr, k, newLeft, newRight, compare);
|
|
111946
|
+
}
|
|
111947
|
+
const t = arr[k];
|
|
111948
|
+
let i = left;
|
|
111949
|
+
let j = right;
|
|
111950
|
+
swap(arr, left, k);
|
|
111951
|
+
if (compare(arr[right], t) > 0)
|
|
111952
|
+
swap(arr, left, right);
|
|
111953
|
+
while (i < j) {
|
|
111954
|
+
swap(arr, i, j);
|
|
111955
|
+
i++;
|
|
111956
|
+
j--;
|
|
111957
|
+
while (compare(arr[i], t) < 0)
|
|
111958
|
+
i++;
|
|
111959
|
+
while (compare(arr[j], t) > 0)
|
|
111960
|
+
j--;
|
|
111961
|
+
}
|
|
111962
|
+
if (compare(arr[left], t) === 0)
|
|
111963
|
+
swap(arr, left, j);
|
|
111964
|
+
else {
|
|
111965
|
+
j++;
|
|
111966
|
+
swap(arr, j, right);
|
|
111967
|
+
}
|
|
111968
|
+
if (j <= k)
|
|
111969
|
+
left = j + 1;
|
|
111970
|
+
if (k <= j)
|
|
111971
|
+
right = j - 1;
|
|
111972
|
+
}
|
|
111973
|
+
}
|
|
111974
|
+
function swap(arr, i, j) {
|
|
111975
|
+
const tmp = arr[i];
|
|
111976
|
+
arr[i] = arr[j];
|
|
111977
|
+
arr[j] = tmp;
|
|
111978
|
+
}
|
|
111979
|
+
function defaultCompare(a, b) {
|
|
111980
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
111981
|
+
}
|
|
111982
|
+
|
|
111983
|
+
// node_modules/rbush/index.js
|
|
111984
|
+
class RBush {
|
|
111985
|
+
constructor(maxEntries = 9) {
|
|
111986
|
+
this._maxEntries = Math.max(4, maxEntries);
|
|
111987
|
+
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
|
|
111988
|
+
this.clear();
|
|
111989
|
+
}
|
|
111990
|
+
all() {
|
|
111991
|
+
return this._all(this.data, []);
|
|
111992
|
+
}
|
|
111993
|
+
search(bbox) {
|
|
111994
|
+
let node = this.data;
|
|
111995
|
+
const result = [];
|
|
111996
|
+
if (!intersects(bbox, node))
|
|
111997
|
+
return result;
|
|
111998
|
+
const toBBox = this.toBBox;
|
|
111999
|
+
const nodesToSearch = [];
|
|
112000
|
+
while (node) {
|
|
112001
|
+
for (let i = 0;i < node.children.length; i++) {
|
|
112002
|
+
const child = node.children[i];
|
|
112003
|
+
const childBBox = node.leaf ? toBBox(child) : child;
|
|
112004
|
+
if (intersects(bbox, childBBox)) {
|
|
112005
|
+
if (node.leaf)
|
|
112006
|
+
result.push(child);
|
|
112007
|
+
else if (contains(bbox, childBBox))
|
|
112008
|
+
this._all(child, result);
|
|
112009
|
+
else
|
|
112010
|
+
nodesToSearch.push(child);
|
|
112011
|
+
}
|
|
112012
|
+
}
|
|
112013
|
+
node = nodesToSearch.pop();
|
|
112014
|
+
}
|
|
112015
|
+
return result;
|
|
112016
|
+
}
|
|
112017
|
+
collides(bbox) {
|
|
112018
|
+
let node = this.data;
|
|
112019
|
+
if (!intersects(bbox, node))
|
|
112020
|
+
return false;
|
|
112021
|
+
const nodesToSearch = [];
|
|
112022
|
+
while (node) {
|
|
112023
|
+
for (let i = 0;i < node.children.length; i++) {
|
|
112024
|
+
const child = node.children[i];
|
|
112025
|
+
const childBBox = node.leaf ? this.toBBox(child) : child;
|
|
112026
|
+
if (intersects(bbox, childBBox)) {
|
|
112027
|
+
if (node.leaf || contains(bbox, childBBox))
|
|
112028
|
+
return true;
|
|
112029
|
+
nodesToSearch.push(child);
|
|
112030
|
+
}
|
|
112031
|
+
}
|
|
112032
|
+
node = nodesToSearch.pop();
|
|
112033
|
+
}
|
|
112034
|
+
return false;
|
|
112035
|
+
}
|
|
112036
|
+
load(data) {
|
|
112037
|
+
if (!(data && data.length))
|
|
112038
|
+
return this;
|
|
112039
|
+
if (data.length < this._minEntries) {
|
|
112040
|
+
for (let i = 0;i < data.length; i++) {
|
|
112041
|
+
this.insert(data[i]);
|
|
112042
|
+
}
|
|
112043
|
+
return this;
|
|
112044
|
+
}
|
|
112045
|
+
let node = this._build(data.slice(), 0, data.length - 1, 0);
|
|
112046
|
+
if (!this.data.children.length) {
|
|
112047
|
+
this.data = node;
|
|
112048
|
+
} else if (this.data.height === node.height) {
|
|
112049
|
+
this._splitRoot(this.data, node);
|
|
112050
|
+
} else {
|
|
112051
|
+
if (this.data.height < node.height) {
|
|
112052
|
+
const tmpNode = this.data;
|
|
112053
|
+
this.data = node;
|
|
112054
|
+
node = tmpNode;
|
|
112055
|
+
}
|
|
112056
|
+
this._insert(node, this.data.height - node.height - 1, true);
|
|
112057
|
+
}
|
|
112058
|
+
return this;
|
|
112059
|
+
}
|
|
112060
|
+
insert(item) {
|
|
112061
|
+
if (item)
|
|
112062
|
+
this._insert(item, this.data.height - 1);
|
|
112063
|
+
return this;
|
|
112064
|
+
}
|
|
112065
|
+
clear() {
|
|
112066
|
+
this.data = createNode([]);
|
|
112067
|
+
return this;
|
|
112068
|
+
}
|
|
112069
|
+
remove(item, equalsFn) {
|
|
112070
|
+
if (!item)
|
|
112071
|
+
return this;
|
|
112072
|
+
let node = this.data;
|
|
112073
|
+
const bbox = this.toBBox(item);
|
|
112074
|
+
const path42 = [];
|
|
112075
|
+
const indexes = [];
|
|
112076
|
+
let i, parent, goingUp;
|
|
112077
|
+
while (node || path42.length) {
|
|
112078
|
+
if (!node) {
|
|
112079
|
+
node = path42.pop();
|
|
112080
|
+
parent = path42[path42.length - 1];
|
|
112081
|
+
i = indexes.pop();
|
|
112082
|
+
goingUp = true;
|
|
112083
|
+
}
|
|
112084
|
+
if (node.leaf) {
|
|
112085
|
+
const index = findItem(item, node.children, equalsFn);
|
|
112086
|
+
if (index !== -1) {
|
|
112087
|
+
node.children.splice(index, 1);
|
|
112088
|
+
path42.push(node);
|
|
112089
|
+
this._condense(path42);
|
|
112090
|
+
return this;
|
|
112091
|
+
}
|
|
112092
|
+
}
|
|
112093
|
+
if (!goingUp && !node.leaf && contains(node, bbox)) {
|
|
112094
|
+
path42.push(node);
|
|
112095
|
+
indexes.push(i);
|
|
112096
|
+
i = 0;
|
|
112097
|
+
parent = node;
|
|
112098
|
+
node = node.children[0];
|
|
112099
|
+
} else if (parent) {
|
|
112100
|
+
i++;
|
|
112101
|
+
node = parent.children[i];
|
|
112102
|
+
goingUp = false;
|
|
112103
|
+
} else
|
|
112104
|
+
node = null;
|
|
112105
|
+
}
|
|
112106
|
+
return this;
|
|
112107
|
+
}
|
|
112108
|
+
toBBox(item) {
|
|
112109
|
+
return item;
|
|
112110
|
+
}
|
|
112111
|
+
compareMinX(a, b) {
|
|
112112
|
+
return a.minX - b.minX;
|
|
112113
|
+
}
|
|
112114
|
+
compareMinY(a, b) {
|
|
112115
|
+
return a.minY - b.minY;
|
|
112116
|
+
}
|
|
112117
|
+
toJSON() {
|
|
112118
|
+
return this.data;
|
|
112119
|
+
}
|
|
112120
|
+
fromJSON(data) {
|
|
112121
|
+
this.data = data;
|
|
112122
|
+
return this;
|
|
112123
|
+
}
|
|
112124
|
+
_all(node, result) {
|
|
112125
|
+
const nodesToSearch = [];
|
|
112126
|
+
while (node) {
|
|
112127
|
+
if (node.leaf)
|
|
112128
|
+
result.push(...node.children);
|
|
112129
|
+
else
|
|
112130
|
+
nodesToSearch.push(...node.children);
|
|
112131
|
+
node = nodesToSearch.pop();
|
|
112132
|
+
}
|
|
112133
|
+
return result;
|
|
112134
|
+
}
|
|
112135
|
+
_build(items, left, right, height) {
|
|
112136
|
+
const N = right - left + 1;
|
|
112137
|
+
let M = this._maxEntries;
|
|
112138
|
+
let node;
|
|
112139
|
+
if (N <= M) {
|
|
112140
|
+
node = createNode(items.slice(left, right + 1));
|
|
112141
|
+
calcBBox(node, this.toBBox);
|
|
112142
|
+
return node;
|
|
112143
|
+
}
|
|
112144
|
+
if (!height) {
|
|
112145
|
+
height = Math.ceil(Math.log(N) / Math.log(M));
|
|
112146
|
+
M = Math.ceil(N / Math.pow(M, height - 1));
|
|
112147
|
+
}
|
|
112148
|
+
node = createNode([]);
|
|
112149
|
+
node.leaf = false;
|
|
112150
|
+
node.height = height;
|
|
112151
|
+
const N2 = Math.ceil(N / M);
|
|
112152
|
+
const N1 = N2 * Math.ceil(Math.sqrt(M));
|
|
112153
|
+
multiSelect(items, left, right, N1, this.compareMinX);
|
|
112154
|
+
for (let i = left;i <= right; i += N1) {
|
|
112155
|
+
const right2 = Math.min(i + N1 - 1, right);
|
|
112156
|
+
multiSelect(items, i, right2, N2, this.compareMinY);
|
|
112157
|
+
for (let j = i;j <= right2; j += N2) {
|
|
112158
|
+
const right3 = Math.min(j + N2 - 1, right2);
|
|
112159
|
+
node.children.push(this._build(items, j, right3, height - 1));
|
|
112160
|
+
}
|
|
112161
|
+
}
|
|
112162
|
+
calcBBox(node, this.toBBox);
|
|
112163
|
+
return node;
|
|
112164
|
+
}
|
|
112165
|
+
_chooseSubtree(bbox, node, level, path42) {
|
|
112166
|
+
while (true) {
|
|
112167
|
+
path42.push(node);
|
|
112168
|
+
if (node.leaf || path42.length - 1 === level)
|
|
112169
|
+
break;
|
|
112170
|
+
let minArea = Infinity;
|
|
112171
|
+
let minEnlargement = Infinity;
|
|
112172
|
+
let targetNode;
|
|
112173
|
+
for (let i = 0;i < node.children.length; i++) {
|
|
112174
|
+
const child = node.children[i];
|
|
112175
|
+
const area = bboxArea(child);
|
|
112176
|
+
const enlargement = enlargedArea(bbox, child) - area;
|
|
112177
|
+
if (enlargement < minEnlargement) {
|
|
112178
|
+
minEnlargement = enlargement;
|
|
112179
|
+
minArea = area < minArea ? area : minArea;
|
|
112180
|
+
targetNode = child;
|
|
112181
|
+
} else if (enlargement === minEnlargement) {
|
|
112182
|
+
if (area < minArea) {
|
|
112183
|
+
minArea = area;
|
|
112184
|
+
targetNode = child;
|
|
112185
|
+
}
|
|
112186
|
+
}
|
|
112187
|
+
}
|
|
112188
|
+
node = targetNode || node.children[0];
|
|
112189
|
+
}
|
|
112190
|
+
return node;
|
|
112191
|
+
}
|
|
112192
|
+
_insert(item, level, isNode) {
|
|
112193
|
+
const bbox = isNode ? item : this.toBBox(item);
|
|
112194
|
+
const insertPath = [];
|
|
112195
|
+
const node = this._chooseSubtree(bbox, this.data, level, insertPath);
|
|
112196
|
+
node.children.push(item);
|
|
112197
|
+
extend(node, bbox);
|
|
112198
|
+
while (level >= 0) {
|
|
112199
|
+
if (insertPath[level].children.length > this._maxEntries) {
|
|
112200
|
+
this._split(insertPath, level);
|
|
112201
|
+
level--;
|
|
112202
|
+
} else
|
|
112203
|
+
break;
|
|
112204
|
+
}
|
|
112205
|
+
this._adjustParentBBoxes(bbox, insertPath, level);
|
|
112206
|
+
}
|
|
112207
|
+
_split(insertPath, level) {
|
|
112208
|
+
const node = insertPath[level];
|
|
112209
|
+
const M = node.children.length;
|
|
112210
|
+
const m = this._minEntries;
|
|
112211
|
+
this._chooseSplitAxis(node, m, M);
|
|
112212
|
+
const splitIndex = this._chooseSplitIndex(node, m, M);
|
|
112213
|
+
const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));
|
|
112214
|
+
newNode.height = node.height;
|
|
112215
|
+
newNode.leaf = node.leaf;
|
|
112216
|
+
calcBBox(node, this.toBBox);
|
|
112217
|
+
calcBBox(newNode, this.toBBox);
|
|
112218
|
+
if (level)
|
|
112219
|
+
insertPath[level - 1].children.push(newNode);
|
|
112220
|
+
else
|
|
112221
|
+
this._splitRoot(node, newNode);
|
|
112222
|
+
}
|
|
112223
|
+
_splitRoot(node, newNode) {
|
|
112224
|
+
this.data = createNode([node, newNode]);
|
|
112225
|
+
this.data.height = node.height + 1;
|
|
112226
|
+
this.data.leaf = false;
|
|
112227
|
+
calcBBox(this.data, this.toBBox);
|
|
112228
|
+
}
|
|
112229
|
+
_chooseSplitIndex(node, m, M) {
|
|
112230
|
+
let index;
|
|
112231
|
+
let minOverlap = Infinity;
|
|
112232
|
+
let minArea = Infinity;
|
|
112233
|
+
for (let i = m;i <= M - m; i++) {
|
|
112234
|
+
const bbox1 = distBBox(node, 0, i, this.toBBox);
|
|
112235
|
+
const bbox2 = distBBox(node, i, M, this.toBBox);
|
|
112236
|
+
const overlap = intersectionArea(bbox1, bbox2);
|
|
112237
|
+
const area = bboxArea(bbox1) + bboxArea(bbox2);
|
|
112238
|
+
if (overlap < minOverlap) {
|
|
112239
|
+
minOverlap = overlap;
|
|
112240
|
+
index = i;
|
|
112241
|
+
minArea = area < minArea ? area : minArea;
|
|
112242
|
+
} else if (overlap === minOverlap) {
|
|
112243
|
+
if (area < minArea) {
|
|
112244
|
+
minArea = area;
|
|
112245
|
+
index = i;
|
|
112246
|
+
}
|
|
112247
|
+
}
|
|
112248
|
+
}
|
|
112249
|
+
return index || M - m;
|
|
112250
|
+
}
|
|
112251
|
+
_chooseSplitAxis(node, m, M) {
|
|
112252
|
+
const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;
|
|
112253
|
+
const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;
|
|
112254
|
+
const xMargin = this._allDistMargin(node, m, M, compareMinX);
|
|
112255
|
+
const yMargin = this._allDistMargin(node, m, M, compareMinY);
|
|
112256
|
+
if (xMargin < yMargin)
|
|
112257
|
+
node.children.sort(compareMinX);
|
|
112258
|
+
}
|
|
112259
|
+
_allDistMargin(node, m, M, compare) {
|
|
112260
|
+
node.children.sort(compare);
|
|
112261
|
+
const toBBox = this.toBBox;
|
|
112262
|
+
const leftBBox = distBBox(node, 0, m, toBBox);
|
|
112263
|
+
const rightBBox = distBBox(node, M - m, M, toBBox);
|
|
112264
|
+
let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);
|
|
112265
|
+
for (let i = m;i < M - m; i++) {
|
|
112266
|
+
const child = node.children[i];
|
|
112267
|
+
extend(leftBBox, node.leaf ? toBBox(child) : child);
|
|
112268
|
+
margin += bboxMargin(leftBBox);
|
|
112269
|
+
}
|
|
112270
|
+
for (let i = M - m - 1;i >= m; i--) {
|
|
112271
|
+
const child = node.children[i];
|
|
112272
|
+
extend(rightBBox, node.leaf ? toBBox(child) : child);
|
|
112273
|
+
margin += bboxMargin(rightBBox);
|
|
112274
|
+
}
|
|
112275
|
+
return margin;
|
|
112276
|
+
}
|
|
112277
|
+
_adjustParentBBoxes(bbox, path42, level) {
|
|
112278
|
+
for (let i = level;i >= 0; i--) {
|
|
112279
|
+
extend(path42[i], bbox);
|
|
112280
|
+
}
|
|
112281
|
+
}
|
|
112282
|
+
_condense(path42) {
|
|
112283
|
+
for (let i = path42.length - 1, siblings;i >= 0; i--) {
|
|
112284
|
+
if (path42[i].children.length === 0) {
|
|
112285
|
+
if (i > 0) {
|
|
112286
|
+
siblings = path42[i - 1].children;
|
|
112287
|
+
siblings.splice(siblings.indexOf(path42[i]), 1);
|
|
112288
|
+
} else
|
|
112289
|
+
this.clear();
|
|
112290
|
+
} else
|
|
112291
|
+
calcBBox(path42[i], this.toBBox);
|
|
112292
|
+
}
|
|
112293
|
+
}
|
|
112294
|
+
}
|
|
112295
|
+
function findItem(item, items, equalsFn) {
|
|
112296
|
+
if (!equalsFn)
|
|
112297
|
+
return items.indexOf(item);
|
|
112298
|
+
for (let i = 0;i < items.length; i++) {
|
|
112299
|
+
if (equalsFn(item, items[i]))
|
|
112300
|
+
return i;
|
|
112301
|
+
}
|
|
112302
|
+
return -1;
|
|
112303
|
+
}
|
|
112304
|
+
function calcBBox(node, toBBox) {
|
|
112305
|
+
distBBox(node, 0, node.children.length, toBBox, node);
|
|
112306
|
+
}
|
|
112307
|
+
function distBBox(node, k, p, toBBox, destNode) {
|
|
112308
|
+
if (!destNode)
|
|
112309
|
+
destNode = createNode(null);
|
|
112310
|
+
destNode.minX = Infinity;
|
|
112311
|
+
destNode.minY = Infinity;
|
|
112312
|
+
destNode.maxX = -Infinity;
|
|
112313
|
+
destNode.maxY = -Infinity;
|
|
112314
|
+
for (let i = k;i < p; i++) {
|
|
112315
|
+
const child = node.children[i];
|
|
112316
|
+
extend(destNode, node.leaf ? toBBox(child) : child);
|
|
112317
|
+
}
|
|
112318
|
+
return destNode;
|
|
112319
|
+
}
|
|
112320
|
+
function extend(a, b) {
|
|
112321
|
+
a.minX = Math.min(a.minX, b.minX);
|
|
112322
|
+
a.minY = Math.min(a.minY, b.minY);
|
|
112323
|
+
a.maxX = Math.max(a.maxX, b.maxX);
|
|
112324
|
+
a.maxY = Math.max(a.maxY, b.maxY);
|
|
112325
|
+
return a;
|
|
112326
|
+
}
|
|
112327
|
+
function compareNodeMinX(a, b) {
|
|
112328
|
+
return a.minX - b.minX;
|
|
112329
|
+
}
|
|
112330
|
+
function compareNodeMinY(a, b) {
|
|
112331
|
+
return a.minY - b.minY;
|
|
112332
|
+
}
|
|
112333
|
+
function bboxArea(a) {
|
|
112334
|
+
return (a.maxX - a.minX) * (a.maxY - a.minY);
|
|
112335
|
+
}
|
|
112336
|
+
function bboxMargin(a) {
|
|
112337
|
+
return a.maxX - a.minX + (a.maxY - a.minY);
|
|
112338
|
+
}
|
|
112339
|
+
function enlargedArea(a, b) {
|
|
112340
|
+
return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
|
|
112341
|
+
}
|
|
112342
|
+
function intersectionArea(a, b) {
|
|
112343
|
+
const minX = Math.max(a.minX, b.minX);
|
|
112344
|
+
const minY = Math.max(a.minY, b.minY);
|
|
112345
|
+
const maxX = Math.min(a.maxX, b.maxX);
|
|
112346
|
+
const maxY = Math.min(a.maxY, b.maxY);
|
|
112347
|
+
return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
|
|
112348
|
+
}
|
|
112349
|
+
function contains(a, b) {
|
|
112350
|
+
return a.minX <= b.minX && a.minY <= b.minY && b.maxX <= a.maxX && b.maxY <= a.maxY;
|
|
112351
|
+
}
|
|
112352
|
+
function intersects(a, b) {
|
|
112353
|
+
return b.minX <= a.maxX && b.minY <= a.maxY && b.maxX >= a.minX && b.maxY >= a.minY;
|
|
112354
|
+
}
|
|
112355
|
+
function createNode(children) {
|
|
112356
|
+
return {
|
|
112357
|
+
children,
|
|
112358
|
+
height: 1,
|
|
112359
|
+
leaf: true,
|
|
112360
|
+
minX: Infinity,
|
|
112361
|
+
minY: Infinity,
|
|
112362
|
+
maxX: -Infinity,
|
|
112363
|
+
maxY: -Infinity
|
|
112364
|
+
};
|
|
112365
|
+
}
|
|
112366
|
+
function multiSelect(arr, left, right, n, compare) {
|
|
112367
|
+
const stack = [left, right];
|
|
112368
|
+
while (stack.length) {
|
|
112369
|
+
right = stack.pop();
|
|
112370
|
+
left = stack.pop();
|
|
112371
|
+
if (right - left <= n)
|
|
112372
|
+
continue;
|
|
112373
|
+
const mid = left + Math.ceil((right - left) / n / 2) * n;
|
|
112374
|
+
quickselect(arr, mid, left, right, compare);
|
|
112375
|
+
stack.push(left, mid, mid, right);
|
|
112376
|
+
}
|
|
112377
|
+
}
|
|
112378
|
+
|
|
111917
112379
|
// node_modules/@tscircuit/circuit-json-placement-analysis/dist/index.js
|
|
111918
112380
|
var CENTER_ANCHOR = "center";
|
|
111919
112381
|
var toNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
@@ -112417,6 +112879,9 @@ var analyzeComponentPlacement = (circuitJson, componentName) => {
|
|
|
112417
112879
|
};
|
|
112418
112880
|
var TOP_ISSUE_LIMIT = 5;
|
|
112419
112881
|
var CENTER_ANCHOR2 = "center";
|
|
112882
|
+
var LARGE_EMPTY_SPACE_THRESHOLD_RATIO = 0.05;
|
|
112883
|
+
var EMPTY_SPACE_SAMPLE_STEP_MM = 5;
|
|
112884
|
+
var GEOMETRY_EPSILON = 0.000001;
|
|
112420
112885
|
var ISSUE_TYPE_ORDER = [
|
|
112421
112886
|
"pad_overlap",
|
|
112422
112887
|
"off_board",
|
|
@@ -112431,6 +112896,8 @@ var fmtNumber2 = (value) => {
|
|
|
112431
112896
|
return value.toFixed(3).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
|
|
112432
112897
|
};
|
|
112433
112898
|
var fmtMm2 = (value) => `${fmtNumber2(value)}mm`;
|
|
112899
|
+
var fmtArea = (value) => `${fmtNumber2(value)}mm^2`;
|
|
112900
|
+
var fmtPercent = (value) => `${fmtNumber2(value)}%`;
|
|
112434
112901
|
var getBoundsFromCenterAndSize = (centerX, centerY, width, height) => ({
|
|
112435
112902
|
min_x: centerX - width / 2,
|
|
112436
112903
|
max_x: centerX + width / 2,
|
|
@@ -112476,6 +112943,22 @@ var getOverlap = (a, b) => {
|
|
|
112476
112943
|
clearance: -Math.min(overlapX, overlapY)
|
|
112477
112944
|
};
|
|
112478
112945
|
};
|
|
112946
|
+
var getBoundsIntersection = (a, b) => {
|
|
112947
|
+
const min_x = Math.max(a.min_x, b.min_x);
|
|
112948
|
+
const max_x = Math.min(a.max_x, b.max_x);
|
|
112949
|
+
const min_y = Math.max(a.min_y, b.min_y);
|
|
112950
|
+
const max_y = Math.min(a.max_y, b.max_y);
|
|
112951
|
+
if (min_x >= max_x || min_y >= max_y)
|
|
112952
|
+
return null;
|
|
112953
|
+
return {
|
|
112954
|
+
min_x,
|
|
112955
|
+
max_x,
|
|
112956
|
+
min_y,
|
|
112957
|
+
max_y,
|
|
112958
|
+
width: max_x - min_x,
|
|
112959
|
+
height: max_y - min_y
|
|
112960
|
+
};
|
|
112961
|
+
};
|
|
112479
112962
|
var layersIntersect = (a, b) => {
|
|
112480
112963
|
const bSet = new Set(b);
|
|
112481
112964
|
return a.some((layer) => bSet.has(layer));
|
|
@@ -112485,6 +112968,14 @@ var getArea = (bounds) => {
|
|
|
112485
112968
|
return Number.POSITIVE_INFINITY;
|
|
112486
112969
|
return bounds.width * bounds.height;
|
|
112487
112970
|
};
|
|
112971
|
+
var toPlacementBounds = (bounds) => ({
|
|
112972
|
+
width: bounds.width,
|
|
112973
|
+
height: bounds.height,
|
|
112974
|
+
min_x: bounds.min_x,
|
|
112975
|
+
max_x: bounds.max_x,
|
|
112976
|
+
min_y: bounds.min_y,
|
|
112977
|
+
max_y: bounds.max_y
|
|
112978
|
+
});
|
|
112488
112979
|
var stripNumericSuffix = (name) => {
|
|
112489
112980
|
const stripped = name.replace(/\d+$/, "");
|
|
112490
112981
|
return stripped.length >= 2 ? stripped : name;
|
|
@@ -112784,6 +113275,263 @@ var buildComponentContexts = (circuitJson) => {
|
|
|
112784
113275
|
boardBounds
|
|
112785
113276
|
};
|
|
112786
113277
|
};
|
|
113278
|
+
var getTopCopperBounds = (component) => {
|
|
113279
|
+
const topPadBounds = component.pads.filter((pad) => pad.layers.includes("top")).map((pad) => pad.bounds);
|
|
113280
|
+
if (topPadBounds.length === 0)
|
|
113281
|
+
return null;
|
|
113282
|
+
return {
|
|
113283
|
+
min_x: Math.min(...topPadBounds.map((bounds) => bounds.min_x)),
|
|
113284
|
+
max_x: Math.max(...topPadBounds.map((bounds) => bounds.max_x)),
|
|
113285
|
+
min_y: Math.min(...topPadBounds.map((bounds) => bounds.min_y)),
|
|
113286
|
+
max_y: Math.max(...topPadBounds.map((bounds) => bounds.max_y)),
|
|
113287
|
+
width: Math.max(...topPadBounds.map((bounds) => bounds.max_x)) - Math.min(...topPadBounds.map((bounds) => bounds.min_x)),
|
|
113288
|
+
height: Math.max(...topPadBounds.map((bounds) => bounds.max_y)) - Math.min(...topPadBounds.map((bounds) => bounds.min_y))
|
|
113289
|
+
};
|
|
113290
|
+
};
|
|
113291
|
+
var getTopOccupancyBounds = (component) => {
|
|
113292
|
+
if (component.layer !== "top")
|
|
113293
|
+
return [];
|
|
113294
|
+
if (component.courtyards.length > 0)
|
|
113295
|
+
return component.courtyards;
|
|
113296
|
+
const topCopperBounds = getTopCopperBounds(component);
|
|
113297
|
+
return topCopperBounds ? [topCopperBounds] : [];
|
|
113298
|
+
};
|
|
113299
|
+
var getUniqueSortedCoordinates = (values) => [...new Set(values)].sort((a, b) => a - b);
|
|
113300
|
+
var createBounds = (min_x, max_x, min_y, max_y) => ({
|
|
113301
|
+
min_x,
|
|
113302
|
+
max_x,
|
|
113303
|
+
min_y,
|
|
113304
|
+
max_y,
|
|
113305
|
+
width: max_x - min_x,
|
|
113306
|
+
height: max_y - min_y
|
|
113307
|
+
});
|
|
113308
|
+
var pointIsInsideBounds = (x, y, bounds) => x > bounds.min_x + GEOMETRY_EPSILON && x < bounds.max_x - GEOMETRY_EPSILON && y > bounds.min_y + GEOMETRY_EPSILON && y < bounds.max_y - GEOMETRY_EPSILON;
|
|
113309
|
+
var buildFlatbushIndex = (boundsList) => {
|
|
113310
|
+
if (boundsList.length === 0)
|
|
113311
|
+
return null;
|
|
113312
|
+
const index = new Flatbush(boundsList.length);
|
|
113313
|
+
for (const bounds of boundsList) {
|
|
113314
|
+
index.add(bounds.min_x, bounds.min_y, bounds.max_x, bounds.max_y);
|
|
113315
|
+
}
|
|
113316
|
+
index.finish();
|
|
113317
|
+
return index;
|
|
113318
|
+
};
|
|
113319
|
+
var boundsOverlapWithArea = (a, b) => getBoundsIntersection(a, b) !== null;
|
|
113320
|
+
var isBoundsEmpty = (bounds, occupiedBounds, occupiedIndex) => {
|
|
113321
|
+
if (bounds.width <= GEOMETRY_EPSILON || bounds.height <= GEOMETRY_EPSILON) {
|
|
113322
|
+
return true;
|
|
113323
|
+
}
|
|
113324
|
+
const candidates = occupiedIndex ? occupiedIndex.search(bounds.min_x, bounds.min_y, bounds.max_x, bounds.max_y) : occupiedBounds.map((_, index) => index);
|
|
113325
|
+
return !candidates.some((candidateIndex) => boundsOverlapWithArea(bounds, occupiedBounds[candidateIndex]));
|
|
113326
|
+
};
|
|
113327
|
+
var isPointOccupied = (x, y, occupiedBounds, occupiedIndex) => {
|
|
113328
|
+
const candidates = occupiedIndex ? occupiedIndex.search(x, y, x, y) : occupiedBounds.map((_, index) => index);
|
|
113329
|
+
return candidates.some((candidateIndex) => pointIsInsideBounds(x, y, occupiedBounds[candidateIndex]));
|
|
113330
|
+
};
|
|
113331
|
+
var getSampleAxisValues = (min, max, step) => {
|
|
113332
|
+
const values = /* @__PURE__ */ new Set([min, max]);
|
|
113333
|
+
let current = min;
|
|
113334
|
+
while (current < max - GEOMETRY_EPSILON) {
|
|
113335
|
+
const next = Math.min(current + step, max);
|
|
113336
|
+
values.add(current);
|
|
113337
|
+
values.add((current + next) / 2);
|
|
113338
|
+
values.add(next);
|
|
113339
|
+
current = next;
|
|
113340
|
+
}
|
|
113341
|
+
return [...values].sort((a, b) => a - b);
|
|
113342
|
+
};
|
|
113343
|
+
var getExpansionDelta = (bounds, boardBounds, direction, occupiedBounds, occupiedIndex) => {
|
|
113344
|
+
const searchIndices = (() => {
|
|
113345
|
+
switch (direction) {
|
|
113346
|
+
case "left":
|
|
113347
|
+
return occupiedIndex ? occupiedIndex.search(boardBounds.min_x, bounds.min_y, bounds.min_x, bounds.max_y) : occupiedBounds.map((_, index) => index);
|
|
113348
|
+
case "right":
|
|
113349
|
+
return occupiedIndex ? occupiedIndex.search(bounds.max_x, bounds.min_y, boardBounds.max_x, bounds.max_y) : occupiedBounds.map((_, index) => index);
|
|
113350
|
+
case "up":
|
|
113351
|
+
return occupiedIndex ? occupiedIndex.search(bounds.min_x, boardBounds.min_y, bounds.max_x, bounds.min_y) : occupiedBounds.map((_, index) => index);
|
|
113352
|
+
case "down":
|
|
113353
|
+
return occupiedIndex ? occupiedIndex.search(bounds.min_x, bounds.max_y, bounds.max_x, boardBounds.max_y) : occupiedBounds.map((_, index) => index);
|
|
113354
|
+
}
|
|
113355
|
+
})();
|
|
113356
|
+
const overlappingCandidates = searchIndices.map((index) => occupiedBounds[index]).filter((candidate) => {
|
|
113357
|
+
if (direction === "left" || direction === "right") {
|
|
113358
|
+
return Math.min(bounds.max_y, candidate.max_y) - Math.max(bounds.min_y, candidate.min_y) > GEOMETRY_EPSILON;
|
|
113359
|
+
}
|
|
113360
|
+
return Math.min(bounds.max_x, candidate.max_x) - Math.max(bounds.min_x, candidate.min_x) > GEOMETRY_EPSILON;
|
|
113361
|
+
});
|
|
113362
|
+
switch (direction) {
|
|
113363
|
+
case "left":
|
|
113364
|
+
return Math.max(0, Math.min(bounds.min_x - boardBounds.min_x, ...overlappingCandidates.filter((candidate) => candidate.max_x <= bounds.min_x + GEOMETRY_EPSILON).map((candidate) => bounds.min_x - candidate.max_x)));
|
|
113365
|
+
case "right":
|
|
113366
|
+
return Math.max(0, Math.min(boardBounds.max_x - bounds.max_x, ...overlappingCandidates.filter((candidate) => candidate.min_x >= bounds.max_x - GEOMETRY_EPSILON).map((candidate) => candidate.min_x - bounds.max_x)));
|
|
113367
|
+
case "up":
|
|
113368
|
+
return Math.max(0, Math.min(bounds.min_y - boardBounds.min_y, ...overlappingCandidates.filter((candidate) => candidate.max_y <= bounds.min_y + GEOMETRY_EPSILON).map((candidate) => bounds.min_y - candidate.max_y)));
|
|
113369
|
+
case "down":
|
|
113370
|
+
return Math.max(0, Math.min(boardBounds.max_y - bounds.max_y, ...overlappingCandidates.filter((candidate) => candidate.min_y >= bounds.max_y - GEOMETRY_EPSILON).map((candidate) => candidate.min_y - bounds.max_y)));
|
|
113371
|
+
}
|
|
113372
|
+
};
|
|
113373
|
+
var expandBounds = (bounds, direction, delta) => {
|
|
113374
|
+
switch (direction) {
|
|
113375
|
+
case "left":
|
|
113376
|
+
return createBounds(bounds.min_x - delta, bounds.max_x, bounds.min_y, bounds.max_y);
|
|
113377
|
+
case "right":
|
|
113378
|
+
return createBounds(bounds.min_x, bounds.max_x + delta, bounds.min_y, bounds.max_y);
|
|
113379
|
+
case "up":
|
|
113380
|
+
return createBounds(bounds.min_x, bounds.max_x, bounds.min_y - delta, bounds.max_y);
|
|
113381
|
+
case "down":
|
|
113382
|
+
return createBounds(bounds.min_x, bounds.max_x, bounds.min_y, bounds.max_y + delta);
|
|
113383
|
+
}
|
|
113384
|
+
};
|
|
113385
|
+
var growBoundsInDirection = (initialBounds, boardBounds, occupiedBounds, occupiedIndex, direction) => {
|
|
113386
|
+
const delta = getExpansionDelta(initialBounds, boardBounds, direction, occupiedBounds, occupiedIndex);
|
|
113387
|
+
if (delta <= GEOMETRY_EPSILON)
|
|
113388
|
+
return initialBounds;
|
|
113389
|
+
const nextBounds = expandBounds(initialBounds, direction, delta);
|
|
113390
|
+
return isBoundsEmpty(nextBounds, occupiedBounds, occupiedIndex) ? nextBounds : initialBounds;
|
|
113391
|
+
};
|
|
113392
|
+
var growRectangleFully = (initialBounds, boardBounds, occupiedBounds, occupiedIndex) => {
|
|
113393
|
+
let currentBounds = initialBounds;
|
|
113394
|
+
while (true) {
|
|
113395
|
+
const candidates = ["left", "right", "up", "down"].map((direction) => {
|
|
113396
|
+
const delta = getExpansionDelta(currentBounds, boardBounds, direction, occupiedBounds, occupiedIndex);
|
|
113397
|
+
if (delta <= GEOMETRY_EPSILON)
|
|
113398
|
+
return null;
|
|
113399
|
+
const nextBounds = expandBounds(currentBounds, direction, delta);
|
|
113400
|
+
if (!isBoundsEmpty(nextBounds, occupiedBounds, occupiedIndex))
|
|
113401
|
+
return null;
|
|
113402
|
+
return {
|
|
113403
|
+
direction,
|
|
113404
|
+
bounds: nextBounds,
|
|
113405
|
+
area: nextBounds.width * nextBounds.height
|
|
113406
|
+
};
|
|
113407
|
+
}).filter((candidate) => Boolean(candidate)).sort((a, b) => {
|
|
113408
|
+
if (b.area !== a.area)
|
|
113409
|
+
return b.area - a.area;
|
|
113410
|
+
return a.direction.localeCompare(b.direction);
|
|
113411
|
+
});
|
|
113412
|
+
const bestCandidate = candidates[0];
|
|
113413
|
+
if (!bestCandidate)
|
|
113414
|
+
return currentBounds;
|
|
113415
|
+
currentBounds = bestCandidate.bounds;
|
|
113416
|
+
}
|
|
113417
|
+
};
|
|
113418
|
+
var getLargestEmptySpaceFromPoint = (x, y, boardBounds, occupiedBounds, occupiedIndex) => {
|
|
113419
|
+
if (isPointOccupied(x, y, occupiedBounds, occupiedIndex))
|
|
113420
|
+
return null;
|
|
113421
|
+
const horizontalSpan = [
|
|
113422
|
+
"left",
|
|
113423
|
+
"right"
|
|
113424
|
+
].reduce((bounds, direction) => growBoundsInDirection(bounds, boardBounds, occupiedBounds, occupiedIndex, direction), createBounds(x, x, y - GEOMETRY_EPSILON, y + GEOMETRY_EPSILON));
|
|
113425
|
+
const verticalSpan = [
|
|
113426
|
+
"up",
|
|
113427
|
+
"down"
|
|
113428
|
+
].reduce((bounds, direction) => growBoundsInDirection(bounds, boardBounds, occupiedBounds, occupiedIndex, direction), createBounds(x - GEOMETRY_EPSILON, x + GEOMETRY_EPSILON, y, y));
|
|
113429
|
+
const horizontalFirst = growRectangleFully(["up", "down"].reduce((bounds, direction) => growBoundsInDirection(bounds, boardBounds, occupiedBounds, occupiedIndex, direction), horizontalSpan), boardBounds, occupiedBounds, occupiedIndex);
|
|
113430
|
+
const verticalFirst = growRectangleFully(["left", "right"].reduce((bounds, direction) => growBoundsInDirection(bounds, boardBounds, occupiedBounds, occupiedIndex, direction), verticalSpan), boardBounds, occupiedBounds, occupiedIndex);
|
|
113431
|
+
const horizontalArea = horizontalFirst.width * horizontalFirst.height;
|
|
113432
|
+
const verticalArea = verticalFirst.width * verticalFirst.height;
|
|
113433
|
+
if (horizontalArea <= GEOMETRY_EPSILON && verticalArea <= GEOMETRY_EPSILON) {
|
|
113434
|
+
return null;
|
|
113435
|
+
}
|
|
113436
|
+
return horizontalArea >= verticalArea ? horizontalFirst : verticalFirst;
|
|
113437
|
+
};
|
|
113438
|
+
var createEmptySpaceIndexItem = (bounds, boardArea, sequenceNumber) => {
|
|
113439
|
+
const area = bounds.width * bounds.height;
|
|
113440
|
+
return {
|
|
113441
|
+
id: `empty-space-${sequenceNumber}`,
|
|
113442
|
+
minX: bounds.min_x,
|
|
113443
|
+
minY: bounds.min_y,
|
|
113444
|
+
maxX: bounds.max_x,
|
|
113445
|
+
maxY: bounds.max_y,
|
|
113446
|
+
area,
|
|
113447
|
+
areaPercent: boardArea === 0 ? 0 : area / boardArea * 100,
|
|
113448
|
+
bounds: toPlacementBounds(bounds)
|
|
113449
|
+
};
|
|
113450
|
+
};
|
|
113451
|
+
var buildLargeEmptySpaces = (boardBounds, occupiedBounds, occupiedIndex, boardArea, thresholdArea) => {
|
|
113452
|
+
const emptySpaceIndex = new RBush;
|
|
113453
|
+
const sampleXs = getSampleAxisValues(boardBounds.min_x, boardBounds.max_x, EMPTY_SPACE_SAMPLE_STEP_MM);
|
|
113454
|
+
const sampleYs = getSampleAxisValues(boardBounds.min_y, boardBounds.max_y, EMPTY_SPACE_SAMPLE_STEP_MM);
|
|
113455
|
+
let sequenceNumber = 0;
|
|
113456
|
+
for (const y of sampleYs) {
|
|
113457
|
+
for (const x of sampleXs) {
|
|
113458
|
+
const bounds = getLargestEmptySpaceFromPoint(x, y, boardBounds, occupiedBounds, occupiedIndex);
|
|
113459
|
+
if (!bounds)
|
|
113460
|
+
continue;
|
|
113461
|
+
const area = bounds.width * bounds.height;
|
|
113462
|
+
if (area <= thresholdArea)
|
|
113463
|
+
continue;
|
|
113464
|
+
const candidate = createEmptySpaceIndexItem(bounds, boardArea, sequenceNumber++);
|
|
113465
|
+
const overlappingSpaces = emptySpaceIndex.search(candidate).filter((space) => boundsOverlapWithArea(bounds, createBounds(space.minX, space.maxX, space.minY, space.maxY)));
|
|
113466
|
+
if (overlappingSpaces.some((space) => space.area >= candidate.area)) {
|
|
113467
|
+
continue;
|
|
113468
|
+
}
|
|
113469
|
+
for (const overlappingSpace of overlappingSpaces) {
|
|
113470
|
+
emptySpaceIndex.remove(overlappingSpace);
|
|
113471
|
+
}
|
|
113472
|
+
emptySpaceIndex.insert(candidate);
|
|
113473
|
+
}
|
|
113474
|
+
}
|
|
113475
|
+
return emptySpaceIndex.all().map(({ area, areaPercent, bounds }) => ({
|
|
113476
|
+
area,
|
|
113477
|
+
areaPercent,
|
|
113478
|
+
bounds
|
|
113479
|
+
})).sort((a, b) => {
|
|
113480
|
+
if (b.area !== a.area)
|
|
113481
|
+
return b.area - a.area;
|
|
113482
|
+
if (a.bounds.min_y !== b.bounds.min_y) {
|
|
113483
|
+
return a.bounds.min_y - b.bounds.min_y;
|
|
113484
|
+
}
|
|
113485
|
+
return a.bounds.min_x - b.bounds.min_x;
|
|
113486
|
+
});
|
|
113487
|
+
};
|
|
113488
|
+
var buildBoardTopLayerReport = (components, boardBounds) => {
|
|
113489
|
+
if (!boardBounds)
|
|
113490
|
+
return null;
|
|
113491
|
+
const boardArea = boardBounds.width * boardBounds.height;
|
|
113492
|
+
const largeEmptySpaceThresholdArea = boardArea * LARGE_EMPTY_SPACE_THRESHOLD_RATIO;
|
|
113493
|
+
const occupancyBounds = components.flatMap((component) => getTopOccupancyBounds(component).map((bounds) => getBoundsIntersection(bounds, boardBounds)).filter((bounds) => Boolean(bounds)));
|
|
113494
|
+
const occupiedIndex = buildFlatbushIndex(occupancyBounds);
|
|
113495
|
+
const xCoords = getUniqueSortedCoordinates([
|
|
113496
|
+
boardBounds.min_x,
|
|
113497
|
+
boardBounds.max_x,
|
|
113498
|
+
...occupancyBounds.flatMap((bounds) => [bounds.min_x, bounds.max_x])
|
|
113499
|
+
]);
|
|
113500
|
+
const yCoords = getUniqueSortedCoordinates([
|
|
113501
|
+
boardBounds.min_y,
|
|
113502
|
+
boardBounds.max_y,
|
|
113503
|
+
...occupancyBounds.flatMap((bounds) => [bounds.min_y, bounds.max_y])
|
|
113504
|
+
]);
|
|
113505
|
+
const occupied = [];
|
|
113506
|
+
let occupiedArea = 0;
|
|
113507
|
+
for (let yi = 0;yi < yCoords.length - 1; yi += 1) {
|
|
113508
|
+
const row = [];
|
|
113509
|
+
for (let xi = 0;xi < xCoords.length - 1; xi += 1) {
|
|
113510
|
+
const cellBounds = {
|
|
113511
|
+
min_x: xCoords[xi],
|
|
113512
|
+
max_x: xCoords[xi + 1],
|
|
113513
|
+
min_y: yCoords[yi],
|
|
113514
|
+
max_y: yCoords[yi + 1],
|
|
113515
|
+
width: xCoords[xi + 1] - xCoords[xi],
|
|
113516
|
+
height: yCoords[yi + 1] - yCoords[yi]
|
|
113517
|
+
};
|
|
113518
|
+
const candidateIndices = occupiedIndex ? occupiedIndex.search(cellBounds.min_x, cellBounds.min_y, cellBounds.max_x, cellBounds.max_y) : occupancyBounds.map((_, index) => index);
|
|
113519
|
+
const isOccupied = candidateIndices.some((candidateIndex) => boundsOverlapWithArea(cellBounds, occupancyBounds[candidateIndex]));
|
|
113520
|
+
row.push(isOccupied);
|
|
113521
|
+
if (isOccupied)
|
|
113522
|
+
occupiedArea += cellBounds.width * cellBounds.height;
|
|
113523
|
+
}
|
|
113524
|
+
occupied.push(row);
|
|
113525
|
+
}
|
|
113526
|
+
const largeEmptySpaces = buildLargeEmptySpaces(boardBounds, occupancyBounds, occupiedIndex, boardArea, largeEmptySpaceThresholdArea);
|
|
113527
|
+
return {
|
|
113528
|
+
boardArea,
|
|
113529
|
+
occupiedArea,
|
|
113530
|
+
utilizationPercent: boardArea === 0 ? 0 : occupiedArea / boardArea * 100,
|
|
113531
|
+
largeEmptySpaceThresholdArea,
|
|
113532
|
+
largeEmptySpaces
|
|
113533
|
+
};
|
|
113534
|
+
};
|
|
112787
113535
|
var buildIssues = (components, boardBounds) => {
|
|
112788
113536
|
const issues = [];
|
|
112789
113537
|
for (const component of components) {
|
|
@@ -113000,6 +113748,7 @@ var formatSourcePlacement = (component) => {
|
|
|
113000
113748
|
}
|
|
113001
113749
|
return bits.join(", ");
|
|
113002
113750
|
};
|
|
113751
|
+
var formatBounds = (bounds) => `bounds=(minX=${fmtMm2(bounds.min_x)}, maxX=${fmtMm2(bounds.max_x)}, minY=${fmtMm2(bounds.min_y)}, maxY=${fmtMm2(bounds.max_y)})`;
|
|
113003
113752
|
var formatResolvedPlacement = (component) => {
|
|
113004
113753
|
const bits = [];
|
|
113005
113754
|
const center = component.resolvedPlacement.center;
|
|
@@ -113008,7 +113757,7 @@ var formatResolvedPlacement = (component) => {
|
|
|
113008
113757
|
bits.push(`center=(${fmtMm2(center.x)}, ${fmtMm2(center.y)}) on ${center.layer}`);
|
|
113009
113758
|
}
|
|
113010
113759
|
if (bounds) {
|
|
113011
|
-
bits.push(
|
|
113760
|
+
bits.push(formatBounds(bounds));
|
|
113012
113761
|
bits.push(`size=(width=${fmtMm2(bounds.width)}, height=${fmtMm2(bounds.height)})`);
|
|
113013
113762
|
}
|
|
113014
113763
|
bits.push(`anchor_alignment="${component.resolvedPlacement.anchorAlignment}"`);
|
|
@@ -113039,6 +113788,20 @@ var formatPlacementAnalysisReport = (report) => {
|
|
|
113039
113788
|
lines.push(`- ${cluster.clusterName}: ${cluster.componentNames.join(", ")}`);
|
|
113040
113789
|
}
|
|
113041
113790
|
}
|
|
113791
|
+
if (report.boardTopLayer) {
|
|
113792
|
+
const emptySpaceThresholdPercent = report.boardTopLayer.boardArea === 0 ? 0 : report.boardTopLayer.largeEmptySpaceThresholdArea / report.boardTopLayer.boardArea * 100;
|
|
113793
|
+
lines.push("");
|
|
113794
|
+
lines.push("board top-layer utilization:");
|
|
113795
|
+
lines.push(`- occupied: ${fmtPercent(report.boardTopLayer.utilizationPercent)} (${fmtArea(report.boardTopLayer.occupiedArea)} of ${fmtArea(report.boardTopLayer.boardArea)})`);
|
|
113796
|
+
if (report.boardTopLayer.largeEmptySpaces.length > 0) {
|
|
113797
|
+
lines.push(`- empty spaces over ${fmtPercent(emptySpaceThresholdPercent)} of board area:`);
|
|
113798
|
+
for (const emptySpace of report.boardTopLayer.largeEmptySpaces) {
|
|
113799
|
+
lines.push(` - ${fmtPercent(emptySpace.areaPercent)} (${fmtArea(emptySpace.area)}); ${formatBounds(emptySpace.bounds)}`);
|
|
113800
|
+
}
|
|
113801
|
+
} else {
|
|
113802
|
+
lines.push(`- empty spaces over ${fmtPercent(emptySpaceThresholdPercent)} of board area: none`);
|
|
113803
|
+
}
|
|
113804
|
+
}
|
|
113042
113805
|
lines.push("");
|
|
113043
113806
|
lines.push("board-edge status:");
|
|
113044
113807
|
for (const component of report.components) {
|
|
@@ -113071,6 +113834,7 @@ var buildPlacementAnalysisReport = (circuitJson) => {
|
|
|
113071
113834
|
const issues = buildIssues(components, boardBounds);
|
|
113072
113835
|
const clusters = buildClusters(components, issues);
|
|
113073
113836
|
const countsByType = buildCountsByType(issues);
|
|
113837
|
+
const boardTopLayer = buildBoardTopLayerReport(components, boardBounds);
|
|
113074
113838
|
return {
|
|
113075
113839
|
summary: {
|
|
113076
113840
|
totalIssueCount: issues.length,
|
|
@@ -113078,6 +113842,7 @@ var buildPlacementAnalysisReport = (circuitJson) => {
|
|
|
113078
113842
|
topIssues: issues.slice(0, TOP_ISSUE_LIMIT),
|
|
113079
113843
|
likelyBadClusters: clusters
|
|
113080
113844
|
},
|
|
113845
|
+
boardTopLayer,
|
|
113081
113846
|
components: buildComponentStatuses(components, boardBounds, issues),
|
|
113082
113847
|
issues
|
|
113083
113848
|
};
|
|
@@ -113109,10 +113874,10 @@ var analyzeAllPlacements = (circuitJson) => {
|
|
|
113109
113874
|
|
|
113110
113875
|
// cli/check/placement/register.ts
|
|
113111
113876
|
import {
|
|
113112
|
-
categorizeErrorOrWarning as
|
|
113877
|
+
categorizeErrorOrWarning as categorizeErrorOrWarning3
|
|
113113
113878
|
} from "@tscircuit/circuit-json-util";
|
|
113114
|
-
var
|
|
113115
|
-
var isPlacementDiagnostic = (issue) =>
|
|
113879
|
+
var normalizeCategory3 = (category) => category === "netlist" || category === "pin_specification" || category === "placement" || category === "routing" ? category : "unknown";
|
|
113880
|
+
var isPlacementDiagnostic = (issue) => normalizeCategory3(categorizeErrorOrWarning3(issue)) === "placement";
|
|
113116
113881
|
var getIssueType = (issue) => issue.error_type ?? issue.warning_type ?? issue.type ?? "unknown_issue";
|
|
113117
113882
|
var getScopedComponentIds = (circuitJson, refdes) => {
|
|
113118
113883
|
const sourceComponentIds = new Set;
|
|
@@ -113210,8 +113975,8 @@ var registerCheck = (program2) => {
|
|
|
113210
113975
|
|
|
113211
113976
|
// node_modules/@tscircuit/circuit-json-routing-analysis/dist/index.js
|
|
113212
113977
|
init_chunk_SK323GHE();
|
|
113213
|
-
import Flatbush from "flatbush";
|
|
113214
113978
|
import Flatbush2 from "flatbush";
|
|
113979
|
+
import Flatbush22 from "flatbush";
|
|
113215
113980
|
import { webcrypto as crypto4 } from "crypto";
|
|
113216
113981
|
import Flatbush3 from "flatbush";
|
|
113217
113982
|
var require_object_hash = __commonJS2({
|
|
@@ -135515,7 +136280,7 @@ var require_is_extendable = __commonJS2({
|
|
|
135515
136280
|
var require_extend_shallow = __commonJS2({
|
|
135516
136281
|
"node_modules/extend-shallow/index.js"(exports2, module2) {
|
|
135517
136282
|
var isObject22 = require_is_extendable();
|
|
135518
|
-
module2.exports = function
|
|
136283
|
+
module2.exports = function extend2(o22) {
|
|
135519
136284
|
if (!isObject22(o22)) {
|
|
135520
136285
|
o22 = {};
|
|
135521
136286
|
}
|
|
@@ -135543,10 +136308,10 @@ var require_extend_shallow = __commonJS2({
|
|
|
135543
136308
|
var require_condense_newlines = __commonJS2({
|
|
135544
136309
|
"node_modules/condense-newlines/index.js"(exports2, module2) {
|
|
135545
136310
|
var isWhitespace2 = require_is_whitespace();
|
|
135546
|
-
var
|
|
136311
|
+
var extend2 = require_extend_shallow();
|
|
135547
136312
|
var typeOf = require_kind_of();
|
|
135548
136313
|
module2.exports = function(str, options) {
|
|
135549
|
-
var opts =
|
|
136314
|
+
var opts = extend2({}, options);
|
|
135550
136315
|
var sep = opts.sep || `
|
|
135551
136316
|
|
|
135552
136317
|
`;
|
|
@@ -135596,7 +136361,7 @@ var require_pretty = __commonJS2({
|
|
|
135596
136361
|
"node_modules/pretty/index.js"(exports2, module2) {
|
|
135597
136362
|
var beautify = require_js();
|
|
135598
136363
|
var condense = require_condense_newlines();
|
|
135599
|
-
var
|
|
136364
|
+
var extend2 = require_extend_shallow();
|
|
135600
136365
|
var defaults = {
|
|
135601
136366
|
unformatted: ["code", "pre", "em", "strong", "span"],
|
|
135602
136367
|
indent_inner_html: true,
|
|
@@ -135606,7 +136371,7 @@ var require_pretty = __commonJS2({
|
|
|
135606
136371
|
`
|
|
135607
136372
|
};
|
|
135608
136373
|
module2.exports = function pretty2(str, options) {
|
|
135609
|
-
var opts =
|
|
136374
|
+
var opts = extend2({}, defaults, options);
|
|
135610
136375
|
str = beautify.html(str, opts);
|
|
135611
136376
|
if (opts.ocd === true) {
|
|
135612
136377
|
if (opts.newlines)
|
|
@@ -135921,7 +136686,7 @@ var translateBounds = (bounds, direction2, distance7) => {
|
|
|
135921
136686
|
var MAX_FREE_SPACE_MM = 5;
|
|
135922
136687
|
var BINARY_SEARCH_ITERATIONS = 10;
|
|
135923
136688
|
var buildComponentSpatialIndex = (components) => {
|
|
135924
|
-
const index = new
|
|
136689
|
+
const index = new Flatbush2(components.length);
|
|
135925
136690
|
for (const component of components) {
|
|
135926
136691
|
index.add(component.bounds.minX, component.bounds.minY, component.bounds.maxX, component.bounds.maxY);
|
|
135927
136692
|
}
|
|
@@ -136848,7 +137613,7 @@ var require_reader4 = __commonJS22({
|
|
|
136848
137613
|
element: "element",
|
|
136849
137614
|
text: "text"
|
|
136850
137615
|
};
|
|
136851
|
-
var
|
|
137616
|
+
var createNode2 = function createNode22(params2) {
|
|
136852
137617
|
return Object.assign({
|
|
136853
137618
|
name: "",
|
|
136854
137619
|
type: NodeType.element,
|
|
@@ -136876,7 +137641,7 @@ var require_reader4 = __commonJS22({
|
|
|
136876
137641
|
current2 = rootNode;
|
|
136877
137642
|
current2.name = data.value;
|
|
136878
137643
|
} else {
|
|
136879
|
-
var node =
|
|
137644
|
+
var node = createNode2({
|
|
136880
137645
|
name: data.value,
|
|
136881
137646
|
parent: current2
|
|
136882
137647
|
});
|
|
@@ -136909,7 +137674,7 @@ var require_reader4 = __commonJS22({
|
|
|
136909
137674
|
break;
|
|
136910
137675
|
case Type.text:
|
|
136911
137676
|
if (current2) {
|
|
136912
|
-
current2.children.push(
|
|
137677
|
+
current2.children.push(createNode2({
|
|
136913
137678
|
type: NodeType.text,
|
|
136914
137679
|
value: data.value,
|
|
136915
137680
|
parent: options.parentNodes ? current2 : null
|
|
@@ -136928,7 +137693,7 @@ var require_reader4 = __commonJS22({
|
|
|
136928
137693
|
reader.reset = function() {
|
|
136929
137694
|
lexer = Lexer.create({ debug: options.debug });
|
|
136930
137695
|
lexer.on("data", handleLexerData);
|
|
136931
|
-
rootNode =
|
|
137696
|
+
rootNode = createNode2();
|
|
136932
137697
|
current2 = null;
|
|
136933
137698
|
attrName = "";
|
|
136934
137699
|
reader.parse = lexer.write;
|
|
@@ -171024,8 +171789,8 @@ var isPointInsidePolygon = (point5, polygon2) => {
|
|
|
171024
171789
|
const yi22 = polygon2[i2].y;
|
|
171025
171790
|
const xj = polygon2[j22].x;
|
|
171026
171791
|
const yj = polygon2[j22].y;
|
|
171027
|
-
const
|
|
171028
|
-
if (
|
|
171792
|
+
const intersects2 = yi22 > point5.y !== yj > point5.y && point5.x < (xj - xi22) * (point5.y - yi22) / (yj - yi22) + xi22;
|
|
171793
|
+
if (intersects2) {
|
|
171029
171794
|
inside2 = !inside2;
|
|
171030
171795
|
}
|
|
171031
171796
|
}
|
|
@@ -192678,7 +193443,7 @@ function getChildFunc(next, adapter) {
|
|
|
192678
193443
|
}
|
|
192679
193444
|
var filters = {
|
|
192680
193445
|
contains(next, text, { adapter }) {
|
|
192681
|
-
return function
|
|
193446
|
+
return function contains2(elem) {
|
|
192682
193447
|
return next(elem) && adapter.getText(elem).includes(text);
|
|
192683
193448
|
};
|
|
192684
193449
|
},
|
|
@@ -194911,7 +195676,7 @@ var DE9IM = class {
|
|
|
194911
195676
|
}
|
|
194912
195677
|
};
|
|
194913
195678
|
function ray_shoot(polygon2, point5) {
|
|
194914
|
-
let
|
|
195679
|
+
let contains2 = undefined;
|
|
194915
195680
|
let ray2 = new Flatten.Ray(point5);
|
|
194916
195681
|
let line2 = new Flatten.Line(ray2.pt, ray2.norm);
|
|
194917
195682
|
const searchBox = new Flatten.Box(ray2.box.xmin - Flatten.DP_TOL, ray2.box.ymin - Flatten.DP_TOL, ray2.box.xmax + Flatten.DP_TOL, ray2.box.ymax + Flatten.DP_TOL);
|
|
@@ -195010,8 +195775,8 @@ function ray_shoot(polygon2, point5) {
|
|
|
195010
195775
|
}
|
|
195011
195776
|
}
|
|
195012
195777
|
}
|
|
195013
|
-
|
|
195014
|
-
return
|
|
195778
|
+
contains2 = counter % 2 === 1 ? INSIDE$2 : OUTSIDE$1;
|
|
195779
|
+
return contains2;
|
|
195015
195780
|
}
|
|
195016
195781
|
function equal(shape1, shape2) {
|
|
195017
195782
|
return relate(shape1, shape2).equal();
|
|
@@ -200377,23 +201142,23 @@ function computeOverlapDistance(compPoly, boardPoly, componentCenter, componentW
|
|
|
200377
201142
|
}
|
|
200378
201143
|
try {
|
|
200379
201144
|
const intersection = BooleanOperations.intersect(compPoly, boardPoly);
|
|
200380
|
-
let
|
|
201145
|
+
let intersectionArea2 = 0;
|
|
200381
201146
|
if (!intersection) {
|
|
200382
|
-
|
|
201147
|
+
intersectionArea2 = 0;
|
|
200383
201148
|
} else if (Array.isArray(intersection)) {
|
|
200384
|
-
|
|
201149
|
+
intersectionArea2 = intersection.reduce((sum, p3) => sum + (typeof p3.area === "function" ? p3.area() : 0), 0);
|
|
200385
201150
|
} else if (typeof intersection.area === "function") {
|
|
200386
|
-
|
|
201151
|
+
intersectionArea2 = intersection.area();
|
|
200387
201152
|
} else {
|
|
200388
|
-
|
|
201153
|
+
intersectionArea2 = 0;
|
|
200389
201154
|
}
|
|
200390
201155
|
const compArea = compPoly.area();
|
|
200391
|
-
if (
|
|
200392
|
-
const overlapRatio = 1 -
|
|
201156
|
+
if (intersectionArea2 > 0 && intersectionArea2 < compArea) {
|
|
201157
|
+
const overlapRatio = 1 - intersectionArea2 / compArea;
|
|
200393
201158
|
const compWidth = Math.abs(componentWidth);
|
|
200394
201159
|
const compHeight = Math.abs(componentHeight);
|
|
200395
201160
|
return Math.min(compWidth, compHeight) * overlapRatio;
|
|
200396
|
-
} else if (
|
|
201161
|
+
} else if (intersectionArea2 === 0) {
|
|
200397
201162
|
return 0.1;
|
|
200398
201163
|
} else {
|
|
200399
201164
|
return 0.1;
|
|
@@ -237108,8 +237873,8 @@ var isPointInRing = (point6, ring2) => {
|
|
|
237108
237873
|
for (const current2 of ring2) {
|
|
237109
237874
|
if (isPointOnSegment2(point6, previous, current2))
|
|
237110
237875
|
return true;
|
|
237111
|
-
const
|
|
237112
|
-
if (
|
|
237876
|
+
const intersects2 = current2.y > point6.y !== previous.y > point6.y && point6.x < (previous.x - current2.x) * (point6.y - current2.y) / (previous.y - current2.y) + current2.x;
|
|
237877
|
+
if (intersects2)
|
|
237113
237878
|
inside2 = !inside2;
|
|
237114
237879
|
previous = current2;
|
|
237115
237880
|
}
|
|
@@ -239299,7 +240064,7 @@ var fmtNumber3 = (value) => {
|
|
|
239299
240064
|
return value.toFixed(3).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
|
|
239300
240065
|
};
|
|
239301
240066
|
var fmtMm22 = (value) => `${value.toFixed(1)}mm`;
|
|
239302
|
-
var
|
|
240067
|
+
var fmtPercent2 = (value) => `${fmtNumber3(value * 100)}%`;
|
|
239303
240068
|
var isCrampedPortPoint = (portPointId) => portPointId?.includes("_cramped") ?? false;
|
|
239304
240069
|
var clamp012 = (value) => Math.max(0, Math.min(1, value));
|
|
239305
240070
|
var roundProbability = (value) => Number.parseFloat(value.toFixed(3));
|
|
@@ -239376,7 +240141,7 @@ var analyzeGlobalCapacityNodes = (nodes, circuitJson) => {
|
|
|
239376
240141
|
const bounds = getBoundsFromNode(node);
|
|
239377
240142
|
return {
|
|
239378
240143
|
lineItemType: "CongestedRegion",
|
|
239379
|
-
probabilityOfFailure:
|
|
240144
|
+
probabilityOfFailure: fmtPercent2(getProbabilityOfFailure(node, maxDensity, maxPortPointCount)),
|
|
239380
240145
|
bounds,
|
|
239381
240146
|
width: getBoundsWidth(bounds),
|
|
239382
240147
|
height: getBoundsHeight(bounds),
|
|
@@ -239665,6 +240430,634 @@ var registerCheckRouting = (program2) => {
|
|
|
239665
240430
|
});
|
|
239666
240431
|
};
|
|
239667
240432
|
|
|
240433
|
+
// node_modules/circuit-json-trace-length-analysis/lib/analyze-circuit-json-trace-length.ts
|
|
240434
|
+
class Trace {
|
|
240435
|
+
id;
|
|
240436
|
+
label;
|
|
240437
|
+
connectionType;
|
|
240438
|
+
connectionTarget;
|
|
240439
|
+
connectionTargetPosition;
|
|
240440
|
+
connectedPins;
|
|
240441
|
+
pinPositions;
|
|
240442
|
+
requirements;
|
|
240443
|
+
points;
|
|
240444
|
+
lengthMm;
|
|
240445
|
+
straightLineDistanceMm;
|
|
240446
|
+
sourceTraceId;
|
|
240447
|
+
displayName;
|
|
240448
|
+
constructor(model) {
|
|
240449
|
+
this.id = model.id;
|
|
240450
|
+
this.label = model.label;
|
|
240451
|
+
this.connectionType = model.connectionType;
|
|
240452
|
+
this.connectionTarget = model.connectionTarget;
|
|
240453
|
+
this.connectionTargetPosition = model.connectionTargetPosition;
|
|
240454
|
+
this.connectedPins = model.connectedPins;
|
|
240455
|
+
this.pinPositions = model.pinPositions;
|
|
240456
|
+
this.requirements = model.requirements;
|
|
240457
|
+
this.points = model.points;
|
|
240458
|
+
this.lengthMm = model.lengthMm;
|
|
240459
|
+
this.straightLineDistanceMm = model.straightLineDistanceMm;
|
|
240460
|
+
this.sourceTraceId = model.sourceTraceId;
|
|
240461
|
+
this.displayName = model.displayName;
|
|
240462
|
+
}
|
|
240463
|
+
toString() {
|
|
240464
|
+
const lines = [
|
|
240465
|
+
`<Trace id="${escapeXml(this.id)}" label="${escapeXml(this.label)}" connectionType="${escapeXml(this.connectionType)}" lengthMm="${formatNumber3(this.lengthMm)}" straightLineDistanceMm="${formatNumber3(this.straightLineDistanceMm)}">`,
|
|
240466
|
+
` <ConnectedPins>`,
|
|
240467
|
+
...this.connectedPins.map((pin) => ` <Pin ref="${escapeXml(pin.ref)}" />`),
|
|
240468
|
+
` </ConnectedPins>`,
|
|
240469
|
+
` <PinPositions>`,
|
|
240470
|
+
...this.pinPositions.map((pin) => renderPinPosition(pin, " ")),
|
|
240471
|
+
` </PinPositions>`,
|
|
240472
|
+
renderConnection(this),
|
|
240473
|
+
renderRequirements(this.requirements, " "),
|
|
240474
|
+
`</Trace>`
|
|
240475
|
+
];
|
|
240476
|
+
if (this.points.length > 0) {
|
|
240477
|
+
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>`);
|
|
240478
|
+
}
|
|
240479
|
+
return lines.join(`
|
|
240480
|
+
`);
|
|
240481
|
+
}
|
|
240482
|
+
}
|
|
240483
|
+
|
|
240484
|
+
class TraceLengthAnalysis {
|
|
240485
|
+
requestedTarget;
|
|
240486
|
+
resolvedTarget;
|
|
240487
|
+
targetKind;
|
|
240488
|
+
totalLengthMm;
|
|
240489
|
+
totalStraightLineDistanceMm;
|
|
240490
|
+
traceCount;
|
|
240491
|
+
#traces;
|
|
240492
|
+
constructor(args) {
|
|
240493
|
+
this.requestedTarget = args.requestedTarget;
|
|
240494
|
+
this.resolvedTarget = args.resolvedTarget;
|
|
240495
|
+
this.targetKind = args.targetKind;
|
|
240496
|
+
this.#traces = [...args.traces];
|
|
240497
|
+
this.totalLengthMm = this.#traces.reduce((sum, trace) => sum + trace.lengthMm, 0);
|
|
240498
|
+
this.totalStraightLineDistanceMm = this.#traces.reduce((sum, trace) => sum + trace.straightLineDistanceMm, 0);
|
|
240499
|
+
this.traceCount = this.#traces.length;
|
|
240500
|
+
}
|
|
240501
|
+
listTraces() {
|
|
240502
|
+
return [...this.#traces];
|
|
240503
|
+
}
|
|
240504
|
+
toString() {
|
|
240505
|
+
const lines = [
|
|
240506
|
+
`<TraceLengthAnalysis requestedTarget="${escapeXml(this.requestedTarget)}" resolvedTarget="${escapeXml(this.resolvedTarget)}" targetKind="${escapeXml(this.targetKind)}" traceCount="${this.traceCount}" totalLengthMm="${formatNumber3(this.totalLengthMm)}" totalStraightLineDistanceMm="${formatNumber3(this.totalStraightLineDistanceMm)}">`,
|
|
240507
|
+
...this.#traces.flatMap((trace) => trace.toString().split(`
|
|
240508
|
+
`).map((line2) => ` ${line2}`)),
|
|
240509
|
+
`</TraceLengthAnalysis>`
|
|
240510
|
+
];
|
|
240511
|
+
return lines.join(`
|
|
240512
|
+
`);
|
|
240513
|
+
}
|
|
240514
|
+
}
|
|
240515
|
+
function analyzeCircuitJsonTraceLength(circuitJson, options) {
|
|
240516
|
+
const index = new CircuitJsonIndex(circuitJson);
|
|
240517
|
+
const target = resolveTarget(index, options.targetPinOrNet);
|
|
240518
|
+
const traces = target.kind === "pin" ? collectPinTraces(index, target) : collectNetTraces(index, target);
|
|
240519
|
+
return new TraceLengthAnalysis({
|
|
240520
|
+
requestedTarget: target.requestedTarget,
|
|
240521
|
+
resolvedTarget: target.resolvedTarget,
|
|
240522
|
+
targetKind: target.kind,
|
|
240523
|
+
traces
|
|
240524
|
+
});
|
|
240525
|
+
}
|
|
240526
|
+
|
|
240527
|
+
class CircuitJsonIndex {
|
|
240528
|
+
componentsById = new Map;
|
|
240529
|
+
componentsByName = new Map;
|
|
240530
|
+
portsById = new Map;
|
|
240531
|
+
portsByComponentId = new Map;
|
|
240532
|
+
netsById = new Map;
|
|
240533
|
+
netsByName = new Map;
|
|
240534
|
+
pcbPortsBySourcePortId = new Map;
|
|
240535
|
+
pcbTracesBySourceTraceId = new Map;
|
|
240536
|
+
pcbTracesByConnectionName = new Map;
|
|
240537
|
+
tracesById = new Map;
|
|
240538
|
+
tracesByPortId = new Map;
|
|
240539
|
+
tracesByNetId = new Map;
|
|
240540
|
+
constructor(circuitJson) {
|
|
240541
|
+
for (const item of circuitJson) {
|
|
240542
|
+
switch (item.type) {
|
|
240543
|
+
case "source_component":
|
|
240544
|
+
this.addComponent(item);
|
|
240545
|
+
break;
|
|
240546
|
+
case "source_port":
|
|
240547
|
+
this.addPort(item);
|
|
240548
|
+
break;
|
|
240549
|
+
case "source_net":
|
|
240550
|
+
this.addNet(item);
|
|
240551
|
+
break;
|
|
240552
|
+
case "pcb_port":
|
|
240553
|
+
this.addPcbPort(item);
|
|
240554
|
+
break;
|
|
240555
|
+
case "pcb_trace":
|
|
240556
|
+
this.addPcbTrace(item);
|
|
240557
|
+
break;
|
|
240558
|
+
case "source_trace":
|
|
240559
|
+
this.addTrace(item);
|
|
240560
|
+
break;
|
|
240561
|
+
default:
|
|
240562
|
+
break;
|
|
240563
|
+
}
|
|
240564
|
+
}
|
|
240565
|
+
}
|
|
240566
|
+
getComponentByName(name) {
|
|
240567
|
+
return this.lookupWithCaseFallback(this.componentsByName, name);
|
|
240568
|
+
}
|
|
240569
|
+
getNetByName(name) {
|
|
240570
|
+
return this.lookupWithCaseFallback(this.netsByName, name);
|
|
240571
|
+
}
|
|
240572
|
+
getPcbPort(sourcePortId) {
|
|
240573
|
+
return this.pcbPortsBySourcePortId.get(sourcePortId)?.[0];
|
|
240574
|
+
}
|
|
240575
|
+
getPortPosition(sourcePortId) {
|
|
240576
|
+
const pcbPort = this.getPcbPort(sourcePortId);
|
|
240577
|
+
if (!pcbPort || typeof pcbPort.x !== "number" || typeof pcbPort.y !== "number") {
|
|
240578
|
+
return null;
|
|
240579
|
+
}
|
|
240580
|
+
const layers = normalizeLayers(pcbPort.layers);
|
|
240581
|
+
return {
|
|
240582
|
+
x: pcbPort.x,
|
|
240583
|
+
y: pcbPort.y,
|
|
240584
|
+
layers
|
|
240585
|
+
};
|
|
240586
|
+
}
|
|
240587
|
+
getPortReference(sourcePortId) {
|
|
240588
|
+
const port = this.portsById.get(sourcePortId);
|
|
240589
|
+
if (!port) {
|
|
240590
|
+
return sourcePortId;
|
|
240591
|
+
}
|
|
240592
|
+
const component = this.componentsById.get(port.source_component_id);
|
|
240593
|
+
const componentName = component?.name ?? port.source_component_id;
|
|
240594
|
+
const portName = port.name ?? String(port.pin_number ?? port.source_port_id);
|
|
240595
|
+
return `${componentName}.${portName}`;
|
|
240596
|
+
}
|
|
240597
|
+
getPcbPathPoints(sourceTraceId) {
|
|
240598
|
+
const candidates = dedupePcbTraces([
|
|
240599
|
+
...this.pcbTracesBySourceTraceId.get(sourceTraceId) ?? [],
|
|
240600
|
+
...this.pcbTracesByConnectionName.get(sourceTraceId) ?? []
|
|
240601
|
+
]);
|
|
240602
|
+
const pcbTraceIds = new Set(candidates.map((trace) => trace.pcb_trace_id));
|
|
240603
|
+
if (candidates.length === 0 || pcbTraceIds.size > 1) {
|
|
240604
|
+
return [];
|
|
240605
|
+
}
|
|
240606
|
+
const routePoints = candidates.flatMap((trace) => normalizePcbTraceRoute(trace));
|
|
240607
|
+
return convertPcbTraceRouteToPath(routePoints);
|
|
240608
|
+
}
|
|
240609
|
+
getConnectedPinsForNet(sourceNetId) {
|
|
240610
|
+
const traces = this.tracesByNetId.get(sourceNetId) ?? [];
|
|
240611
|
+
const sourcePortIds = new Set;
|
|
240612
|
+
for (const trace of traces) {
|
|
240613
|
+
for (const sourcePortId of trace.connected_source_port_ids ?? []) {
|
|
240614
|
+
sourcePortIds.add(sourcePortId);
|
|
240615
|
+
}
|
|
240616
|
+
}
|
|
240617
|
+
return [...sourcePortIds].map((sourcePortId) => this.toConnectedPin(sourcePortId));
|
|
240618
|
+
}
|
|
240619
|
+
toConnectedPin(sourcePortId) {
|
|
240620
|
+
const position2 = this.getPortPosition(sourcePortId);
|
|
240621
|
+
return {
|
|
240622
|
+
ref: this.getPortReference(sourcePortId),
|
|
240623
|
+
x: position2?.x ?? null,
|
|
240624
|
+
y: position2?.y ?? null,
|
|
240625
|
+
layers: position2?.layers ?? []
|
|
240626
|
+
};
|
|
240627
|
+
}
|
|
240628
|
+
addComponent(component) {
|
|
240629
|
+
this.componentsById.set(component.source_component_id, component);
|
|
240630
|
+
const name = component.name;
|
|
240631
|
+
if (!name) {
|
|
240632
|
+
return;
|
|
240633
|
+
}
|
|
240634
|
+
this.addLookup(this.componentsByName, name, component);
|
|
240635
|
+
}
|
|
240636
|
+
addPort(port) {
|
|
240637
|
+
this.portsById.set(port.source_port_id, port);
|
|
240638
|
+
this.addLookup(this.portsByComponentId, port.source_component_id, port);
|
|
240639
|
+
}
|
|
240640
|
+
addNet(net) {
|
|
240641
|
+
this.netsById.set(net.source_net_id, net);
|
|
240642
|
+
const name = net.name;
|
|
240643
|
+
if (!name) {
|
|
240644
|
+
return;
|
|
240645
|
+
}
|
|
240646
|
+
this.addLookup(this.netsByName, name, net);
|
|
240647
|
+
}
|
|
240648
|
+
addPcbPort(pcbPort) {
|
|
240649
|
+
this.addLookup(this.pcbPortsBySourcePortId, pcbPort.source_port_id, pcbPort);
|
|
240650
|
+
}
|
|
240651
|
+
addPcbTrace(pcbTrace) {
|
|
240652
|
+
if (pcbTrace.source_trace_id) {
|
|
240653
|
+
this.addLookup(this.pcbTracesBySourceTraceId, pcbTrace.source_trace_id, pcbTrace);
|
|
240654
|
+
}
|
|
240655
|
+
if (pcbTrace.connection_name) {
|
|
240656
|
+
this.addLookup(this.pcbTracesByConnectionName, pcbTrace.connection_name, pcbTrace);
|
|
240657
|
+
}
|
|
240658
|
+
}
|
|
240659
|
+
addTrace(trace) {
|
|
240660
|
+
this.tracesById.set(trace.source_trace_id, trace);
|
|
240661
|
+
for (const sourcePortId of trace.connected_source_port_ids ?? []) {
|
|
240662
|
+
this.addLookup(this.tracesByPortId, sourcePortId, trace);
|
|
240663
|
+
}
|
|
240664
|
+
for (const sourceNetId of trace.connected_source_net_ids ?? []) {
|
|
240665
|
+
this.addLookup(this.tracesByNetId, sourceNetId, trace);
|
|
240666
|
+
}
|
|
240667
|
+
}
|
|
240668
|
+
addLookup(map, key, value) {
|
|
240669
|
+
const existing = map.get(key) ?? [];
|
|
240670
|
+
existing.push(value);
|
|
240671
|
+
map.set(key, existing);
|
|
240672
|
+
}
|
|
240673
|
+
lookupWithCaseFallback(map, key) {
|
|
240674
|
+
const exactMatch = map.get(key);
|
|
240675
|
+
if (exactMatch?.length) {
|
|
240676
|
+
return exactMatch;
|
|
240677
|
+
}
|
|
240678
|
+
const loweredKey = key.toLowerCase();
|
|
240679
|
+
const fuzzyMatches = [];
|
|
240680
|
+
for (const [candidateKey, values] of map.entries()) {
|
|
240681
|
+
if (candidateKey.toLowerCase() === loweredKey) {
|
|
240682
|
+
fuzzyMatches.push(...values);
|
|
240683
|
+
}
|
|
240684
|
+
}
|
|
240685
|
+
return fuzzyMatches;
|
|
240686
|
+
}
|
|
240687
|
+
}
|
|
240688
|
+
function resolveTarget(index, targetPinOrNet) {
|
|
240689
|
+
if (targetPinOrNet.includes(".")) {
|
|
240690
|
+
if (targetPinOrNet.startsWith("net.")) {
|
|
240691
|
+
const netName = targetPinOrNet.slice("net.".length);
|
|
240692
|
+
const net = resolveNetByName(index, netName);
|
|
240693
|
+
return {
|
|
240694
|
+
kind: "net",
|
|
240695
|
+
requestedTarget: targetPinOrNet,
|
|
240696
|
+
resolvedTarget: `net.${net.name ?? net.source_net_id}`,
|
|
240697
|
+
net
|
|
240698
|
+
};
|
|
240699
|
+
}
|
|
240700
|
+
return resolvePinTarget(index, targetPinOrNet);
|
|
240701
|
+
}
|
|
240702
|
+
const matchingNet = resolveOptionalNetByName(index, targetPinOrNet);
|
|
240703
|
+
if (matchingNet) {
|
|
240704
|
+
const resolvedTarget = `net.${matchingNet.name ?? matchingNet.source_net_id}`;
|
|
240705
|
+
console.log(`inferring ${resolvedTarget}`);
|
|
240706
|
+
return {
|
|
240707
|
+
kind: "net",
|
|
240708
|
+
requestedTarget: targetPinOrNet,
|
|
240709
|
+
resolvedTarget,
|
|
240710
|
+
net: matchingNet
|
|
240711
|
+
};
|
|
240712
|
+
}
|
|
240713
|
+
const matchingPin = resolveOptionalPinByLabel(index, targetPinOrNet);
|
|
240714
|
+
if (matchingPin) {
|
|
240715
|
+
const resolvedTarget = index.getPortReference(matchingPin.source_port_id);
|
|
240716
|
+
console.log(`inferring ${resolvedTarget}`);
|
|
240717
|
+
return {
|
|
240718
|
+
kind: "pin",
|
|
240719
|
+
requestedTarget: targetPinOrNet,
|
|
240720
|
+
resolvedTarget,
|
|
240721
|
+
port: matchingPin
|
|
240722
|
+
};
|
|
240723
|
+
}
|
|
240724
|
+
throw new Error(`Unable to resolve target "${targetPinOrNet}" to a pin or net`);
|
|
240725
|
+
}
|
|
240726
|
+
function resolvePinTarget(index, targetPinOrNet) {
|
|
240727
|
+
const [componentName, ...pinParts] = targetPinOrNet.split(".");
|
|
240728
|
+
const pinLabelOrNumber = pinParts.join(".");
|
|
240729
|
+
if (!componentName || !pinLabelOrNumber) {
|
|
240730
|
+
throw new Error(`Invalid pin target "${targetPinOrNet}"`);
|
|
240731
|
+
}
|
|
240732
|
+
const components = index.getComponentByName(componentName);
|
|
240733
|
+
if (components.length !== 1) {
|
|
240734
|
+
throw new Error(components.length === 0 ? `Unable to find component "${componentName}"` : `Component "${componentName}" is ambiguous`);
|
|
240735
|
+
}
|
|
240736
|
+
const component = components[0];
|
|
240737
|
+
if (!component) {
|
|
240738
|
+
throw new Error(`Unable to find component "${componentName}"`);
|
|
240739
|
+
}
|
|
240740
|
+
const ports = index.portsByComponentId.get(component.source_component_id) ?? [];
|
|
240741
|
+
const matchingPorts = ports.filter((port2) => portMatchesLabel(port2, pinLabelOrNumber));
|
|
240742
|
+
if (matchingPorts.length !== 1) {
|
|
240743
|
+
throw new Error(matchingPorts.length === 0 ? `Unable to find pin "${pinLabelOrNumber}" on ${componentName}` : `Pin "${pinLabelOrNumber}" on ${componentName} is ambiguous`);
|
|
240744
|
+
}
|
|
240745
|
+
const port = matchingPorts[0];
|
|
240746
|
+
if (!port) {
|
|
240747
|
+
throw new Error(`Unable to find pin "${pinLabelOrNumber}" on ${componentName}`);
|
|
240748
|
+
}
|
|
240749
|
+
return {
|
|
240750
|
+
kind: "pin",
|
|
240751
|
+
requestedTarget: targetPinOrNet,
|
|
240752
|
+
resolvedTarget: index.getPortReference(port.source_port_id),
|
|
240753
|
+
port
|
|
240754
|
+
};
|
|
240755
|
+
}
|
|
240756
|
+
function resolveOptionalPinByLabel(index, pinLabelOrNumber) {
|
|
240757
|
+
const matchingPorts = [...index.portsById.values()].filter((port) => portMatchesLabel(port, pinLabelOrNumber));
|
|
240758
|
+
if (matchingPorts.length !== 1) {
|
|
240759
|
+
return null;
|
|
240760
|
+
}
|
|
240761
|
+
return matchingPorts[0] ?? null;
|
|
240762
|
+
}
|
|
240763
|
+
function resolveNetByName(index, netName) {
|
|
240764
|
+
const matchingNets = index.getNetByName(netName);
|
|
240765
|
+
if (matchingNets.length !== 1) {
|
|
240766
|
+
throw new Error(matchingNets.length === 0 ? `Unable to find net "${netName}"` : `Net "${netName}" is ambiguous`);
|
|
240767
|
+
}
|
|
240768
|
+
const net = matchingNets[0];
|
|
240769
|
+
if (!net) {
|
|
240770
|
+
throw new Error(`Unable to find net "${netName}"`);
|
|
240771
|
+
}
|
|
240772
|
+
return net;
|
|
240773
|
+
}
|
|
240774
|
+
function resolveOptionalNetByName(index, netName) {
|
|
240775
|
+
const matchingNets = index.getNetByName(netName);
|
|
240776
|
+
if (matchingNets.length !== 1) {
|
|
240777
|
+
return null;
|
|
240778
|
+
}
|
|
240779
|
+
return matchingNets[0] ?? null;
|
|
240780
|
+
}
|
|
240781
|
+
function collectPinTraces(index, target) {
|
|
240782
|
+
const traces = index.tracesByPortId.get(target.port.source_port_id) ?? [];
|
|
240783
|
+
return traces.flatMap((trace) => {
|
|
240784
|
+
const netIds = trace.connected_source_net_ids ?? [];
|
|
240785
|
+
if (netIds.length > 0) {
|
|
240786
|
+
return netIds.map((sourceNetId) => createNetTraceModel(index, {
|
|
240787
|
+
trace,
|
|
240788
|
+
sourceNetId,
|
|
240789
|
+
focusSourcePortId: target.port.source_port_id
|
|
240790
|
+
}));
|
|
240791
|
+
}
|
|
240792
|
+
return [
|
|
240793
|
+
createDirectTraceModel(index, {
|
|
240794
|
+
trace,
|
|
240795
|
+
focusSourcePortId: target.port.source_port_id
|
|
240796
|
+
})
|
|
240797
|
+
];
|
|
240798
|
+
}).sort(compareTraceModels).map((model) => new Trace(model));
|
|
240799
|
+
}
|
|
240800
|
+
function collectNetTraces(index, target) {
|
|
240801
|
+
const traces = index.tracesByNetId.get(target.net.source_net_id) ?? [];
|
|
240802
|
+
return traces.flatMap((trace) => {
|
|
240803
|
+
const connectedSourcePortIds = trace.connected_source_port_ids ?? [];
|
|
240804
|
+
if (connectedSourcePortIds.length === 0) {
|
|
240805
|
+
return [];
|
|
240806
|
+
}
|
|
240807
|
+
return connectedSourcePortIds.map((focusSourcePortId) => createNetTraceModel(index, {
|
|
240808
|
+
trace,
|
|
240809
|
+
sourceNetId: target.net.source_net_id,
|
|
240810
|
+
focusSourcePortId
|
|
240811
|
+
}));
|
|
240812
|
+
}).sort(compareTraceModels).map((model) => new Trace(model));
|
|
240813
|
+
}
|
|
240814
|
+
function createDirectTraceModel(index, args) {
|
|
240815
|
+
const connectedSourcePortIds = args.trace.connected_source_port_ids ?? [];
|
|
240816
|
+
if (connectedSourcePortIds.length === 0) {
|
|
240817
|
+
throw new Error(`Trace ${args.trace.source_trace_id} has no connected ports`);
|
|
240818
|
+
}
|
|
240819
|
+
const orderedPortIds = prioritizeFocus(connectedSourcePortIds, args.focusSourcePortId);
|
|
240820
|
+
const firstPortId = orderedPortIds[0];
|
|
240821
|
+
const secondPortId = orderedPortIds[1] ?? orderedPortIds[0];
|
|
240822
|
+
if (!firstPortId || !secondPortId) {
|
|
240823
|
+
throw new Error(`Trace ${args.trace.source_trace_id} is missing connected ports`);
|
|
240824
|
+
}
|
|
240825
|
+
const firstPosition = requirePortPosition(index, firstPortId);
|
|
240826
|
+
const secondPosition = requirePortPosition(index, secondPortId);
|
|
240827
|
+
const secondLayer = primaryLayer(secondPosition.layers);
|
|
240828
|
+
const connectedPins = orderedPortIds.map((sourcePortId) => index.toConnectedPin(sourcePortId));
|
|
240829
|
+
const connectionTarget4 = index.getPortReference(secondPortId);
|
|
240830
|
+
const straightLineDistanceMm = distanceBetween(firstPosition, secondPosition);
|
|
240831
|
+
const points = index.getPcbPathPoints(args.trace.source_trace_id);
|
|
240832
|
+
const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
|
|
240833
|
+
return {
|
|
240834
|
+
id: args.trace.source_trace_id,
|
|
240835
|
+
label: `${index.getPortReference(firstPortId)} -> ${connectionTarget4}`,
|
|
240836
|
+
connectionType: "direct connection",
|
|
240837
|
+
connectionTarget: connectionTarget4,
|
|
240838
|
+
connectionTargetPosition: {
|
|
240839
|
+
x: secondPosition.x,
|
|
240840
|
+
y: secondPosition.y,
|
|
240841
|
+
layer: secondLayer
|
|
240842
|
+
},
|
|
240843
|
+
connectedPins,
|
|
240844
|
+
pinPositions: connectedPins,
|
|
240845
|
+
requirements: {
|
|
240846
|
+
maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
|
|
240847
|
+
},
|
|
240848
|
+
points,
|
|
240849
|
+
lengthMm,
|
|
240850
|
+
straightLineDistanceMm,
|
|
240851
|
+
sourceTraceId: args.trace.source_trace_id,
|
|
240852
|
+
displayName: args.trace.display_name ?? null
|
|
240853
|
+
};
|
|
240854
|
+
}
|
|
240855
|
+
function createNetTraceModel(index, args) {
|
|
240856
|
+
const net = index.netsById.get(args.sourceNetId);
|
|
240857
|
+
if (!net) {
|
|
240858
|
+
throw new Error(`Unable to find net ${args.sourceNetId}`);
|
|
240859
|
+
}
|
|
240860
|
+
const focusPosition = requirePortPosition(index, args.focusSourcePortId);
|
|
240861
|
+
const focusLayer = primaryLayer(focusPosition.layers);
|
|
240862
|
+
const connectedPins = index.getConnectedPinsForNet(args.sourceNetId).sort((a2, b) => a2.ref.localeCompare(b.ref));
|
|
240863
|
+
const hub = inferNetHub(connectedPins, focusLayer);
|
|
240864
|
+
const focusPin = index.toConnectedPin(args.focusSourcePortId);
|
|
240865
|
+
const points = index.getPcbPathPoints(args.trace.source_trace_id);
|
|
240866
|
+
const straightLineDistanceMm = distanceBetween(focusPosition, hub);
|
|
240867
|
+
const lengthMm = points.length > 1 ? measurePathLength(points) : straightLineDistanceMm;
|
|
240868
|
+
return {
|
|
240869
|
+
id: `${args.trace.source_trace_id}:${args.focusSourcePortId}`,
|
|
240870
|
+
label: `${focusPin.ref} -> net.${net.name ?? net.source_net_id}`,
|
|
240871
|
+
connectionType: "via net",
|
|
240872
|
+
connectionTarget: `net.${net.name ?? net.source_net_id}`,
|
|
240873
|
+
connectionTargetPosition: hub,
|
|
240874
|
+
connectedPins: prioritizePin(focusPin, connectedPins),
|
|
240875
|
+
pinPositions: prioritizePin(focusPin, connectedPins),
|
|
240876
|
+
requirements: {
|
|
240877
|
+
maxLengthMm: typeof args.trace.max_length === "number" ? args.trace.max_length : null
|
|
240878
|
+
},
|
|
240879
|
+
points,
|
|
240880
|
+
lengthMm,
|
|
240881
|
+
straightLineDistanceMm,
|
|
240882
|
+
sourceTraceId: args.trace.source_trace_id,
|
|
240883
|
+
displayName: args.trace.display_name ?? null
|
|
240884
|
+
};
|
|
240885
|
+
}
|
|
240886
|
+
function requirePortPosition(index, sourcePortId) {
|
|
240887
|
+
const position2 = index.getPortPosition(sourcePortId);
|
|
240888
|
+
if (!position2) {
|
|
240889
|
+
throw new Error(`Unable to analyze ${index.getPortReference(sourcePortId)} because it has no pcb_port position`);
|
|
240890
|
+
}
|
|
240891
|
+
return position2;
|
|
240892
|
+
}
|
|
240893
|
+
function inferNetHub(connectedPins, fallbackLayer) {
|
|
240894
|
+
const positionedPins = connectedPins.filter((pin) => typeof pin.x === "number" && typeof pin.y === "number");
|
|
240895
|
+
if (positionedPins.length === 0) {
|
|
240896
|
+
return {
|
|
240897
|
+
x: 0,
|
|
240898
|
+
y: 0,
|
|
240899
|
+
layer: fallbackLayer
|
|
240900
|
+
};
|
|
240901
|
+
}
|
|
240902
|
+
const x3 = positionedPins.reduce((sum, pin) => sum + pin.x, 0) / positionedPins.length;
|
|
240903
|
+
const y4 = positionedPins.reduce((sum, pin) => sum + pin.y, 0) / positionedPins.length;
|
|
240904
|
+
const layerCounts = new Map;
|
|
240905
|
+
for (const pin of positionedPins) {
|
|
240906
|
+
const layer2 = pin.layers[0] ?? fallbackLayer;
|
|
240907
|
+
layerCounts.set(layer2, (layerCounts.get(layer2) ?? 0) + 1);
|
|
240908
|
+
}
|
|
240909
|
+
const sortedLayers = [...layerCounts.entries()].sort((left, right) => {
|
|
240910
|
+
if (right[1] !== left[1]) {
|
|
240911
|
+
return right[1] - left[1];
|
|
240912
|
+
}
|
|
240913
|
+
return left[0].localeCompare(right[0]);
|
|
240914
|
+
});
|
|
240915
|
+
const layer = sortedLayers[0]?.[0] ?? fallbackLayer;
|
|
240916
|
+
return { x: x3, y: y4, layer };
|
|
240917
|
+
}
|
|
240918
|
+
function measurePathLength(points) {
|
|
240919
|
+
let lengthMm = 0;
|
|
240920
|
+
for (let index = 1;index < points.length; index += 1) {
|
|
240921
|
+
const previous = points[index - 1];
|
|
240922
|
+
const current2 = points[index];
|
|
240923
|
+
if (!previous || !current2) {
|
|
240924
|
+
continue;
|
|
240925
|
+
}
|
|
240926
|
+
lengthMm += distanceBetween(previous, current2);
|
|
240927
|
+
}
|
|
240928
|
+
return lengthMm;
|
|
240929
|
+
}
|
|
240930
|
+
function dedupePcbTraces(pcbTraces) {
|
|
240931
|
+
const seen = new Set;
|
|
240932
|
+
const deduped = [];
|
|
240933
|
+
for (const pcbTrace of pcbTraces) {
|
|
240934
|
+
if (seen.has(pcbTrace)) {
|
|
240935
|
+
continue;
|
|
240936
|
+
}
|
|
240937
|
+
seen.add(pcbTrace);
|
|
240938
|
+
deduped.push(pcbTrace);
|
|
240939
|
+
}
|
|
240940
|
+
return deduped;
|
|
240941
|
+
}
|
|
240942
|
+
function normalizePcbTraceRoute(pcbTrace) {
|
|
240943
|
+
if (!Array.isArray(pcbTrace.route)) {
|
|
240944
|
+
return [];
|
|
240945
|
+
}
|
|
240946
|
+
return pcbTrace.route.filter((point5) => typeof point5.x === "number" && typeof point5.y === "number").map((point5) => ({
|
|
240947
|
+
...point5,
|
|
240948
|
+
layer: typeof point5.layer === "string" && point5.layer.length > 0 ? point5.layer : typeof pcbTrace.layer === "string" && pcbTrace.layer.length > 0 ? pcbTrace.layer : "top"
|
|
240949
|
+
}));
|
|
240950
|
+
}
|
|
240951
|
+
function convertPcbTraceRouteToPath(routePoints) {
|
|
240952
|
+
const deduped = routePoints.filter((point5, index) => {
|
|
240953
|
+
const previous = routePoints[index - 1];
|
|
240954
|
+
return !previous || previous.x !== point5.x || previous.y !== point5.y || previous.layer !== point5.layer;
|
|
240955
|
+
});
|
|
240956
|
+
return deduped.map((point5, index) => ({
|
|
240957
|
+
x: point5.x,
|
|
240958
|
+
y: point5.y,
|
|
240959
|
+
layer: point5.layer,
|
|
240960
|
+
kind: classifyPcbTracePoint(point5, index, deduped.length)
|
|
240961
|
+
}));
|
|
240962
|
+
}
|
|
240963
|
+
function classifyPcbTracePoint(point5, index, pointCount) {
|
|
240964
|
+
if (point5.route_type === "via") {
|
|
240965
|
+
return "via";
|
|
240966
|
+
}
|
|
240967
|
+
if (index === 0 || index === pointCount - 1 || point5.start_pcb_port_id || point5.end_pcb_port_id) {
|
|
240968
|
+
return "endpoint";
|
|
240969
|
+
}
|
|
240970
|
+
return "track";
|
|
240971
|
+
}
|
|
240972
|
+
function renderPinPosition(pin, indent) {
|
|
240973
|
+
if (typeof pin.x !== "number" || typeof pin.y !== "number") {
|
|
240974
|
+
return `${indent}<Pin ref="${escapeXml(pin.ref)}" position="unavailable" />`;
|
|
240975
|
+
}
|
|
240976
|
+
const layers = pin.layers.length > 0 ? pin.layers.join(",") : "unknown";
|
|
240977
|
+
return `${indent}<Pin ref="${escapeXml(pin.ref)}" x="${formatNumber3(pin.x)}" y="${formatNumber3(pin.y)}" layers="${escapeXml(layers)}" />`;
|
|
240978
|
+
}
|
|
240979
|
+
function renderConnection(trace) {
|
|
240980
|
+
if (trace.connectionTargetPosition) {
|
|
240981
|
+
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)}" />`;
|
|
240982
|
+
}
|
|
240983
|
+
return ` <Connection kind="${escapeXml(trace.connectionType)}" target="${escapeXml(trace.connectionTarget)}" />`;
|
|
240984
|
+
}
|
|
240985
|
+
function renderRequirements(requirements, indent) {
|
|
240986
|
+
if (typeof requirements.maxLengthMm === "number") {
|
|
240987
|
+
return [
|
|
240988
|
+
`${indent}<TraceRequirements>`,
|
|
240989
|
+
`${indent} <MaxLengthMm>${formatNumber3(requirements.maxLengthMm)}</MaxLengthMm>`,
|
|
240990
|
+
`${indent}</TraceRequirements>`
|
|
240991
|
+
].join(`
|
|
240992
|
+
`);
|
|
240993
|
+
}
|
|
240994
|
+
return `${indent}<TraceRequirements none />`;
|
|
240995
|
+
}
|
|
240996
|
+
function portMatchesLabel(port, label) {
|
|
240997
|
+
const loweredLabel = label.toLowerCase();
|
|
240998
|
+
const portName = port.name?.toLowerCase();
|
|
240999
|
+
const pinNumber = String(port.pin_number ?? "").toLowerCase();
|
|
241000
|
+
const portHints2 = (port.port_hints ?? []).map((hint) => hint.toLowerCase());
|
|
241001
|
+
return portName === loweredLabel || pinNumber === loweredLabel || portHints2.includes(loweredLabel);
|
|
241002
|
+
}
|
|
241003
|
+
function prioritizeFocus(portIds, focusSourcePortId) {
|
|
241004
|
+
const remainder = portIds.filter((portId) => portId !== focusSourcePortId);
|
|
241005
|
+
return [focusSourcePortId, ...remainder];
|
|
241006
|
+
}
|
|
241007
|
+
function prioritizePin(focusPin, pins) {
|
|
241008
|
+
const remainder = pins.filter((pin) => pin.ref !== focusPin.ref);
|
|
241009
|
+
return [focusPin, ...remainder];
|
|
241010
|
+
}
|
|
241011
|
+
function compareTraceModels(left, right) {
|
|
241012
|
+
return left.label.localeCompare(right.label);
|
|
241013
|
+
}
|
|
241014
|
+
function normalizeLayers(layers) {
|
|
241015
|
+
if (!Array.isArray(layers)) {
|
|
241016
|
+
return ["top"];
|
|
241017
|
+
}
|
|
241018
|
+
const normalizedLayers = layers.filter((layer) => typeof layer === "string" && layer.length > 0);
|
|
241019
|
+
return normalizedLayers.length > 0 ? normalizedLayers : ["top"];
|
|
241020
|
+
}
|
|
241021
|
+
function primaryLayer(layers) {
|
|
241022
|
+
return layers[0] ?? "top";
|
|
241023
|
+
}
|
|
241024
|
+
function distanceBetween(left, right) {
|
|
241025
|
+
return Math.hypot(right.x - left.x, right.y - left.y);
|
|
241026
|
+
}
|
|
241027
|
+
function formatNumber3(value) {
|
|
241028
|
+
return value.toFixed(2);
|
|
241029
|
+
}
|
|
241030
|
+
function escapeXml(value) {
|
|
241031
|
+
return value.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<").replaceAll(">", ">");
|
|
241032
|
+
}
|
|
241033
|
+
// cli/check/trace-length/register.ts
|
|
241034
|
+
var checkTraceLength = async (pinOrNetRef, file) => {
|
|
241035
|
+
const resolvedInputFilePath = await resolveCheckInputFilePath(file);
|
|
241036
|
+
const circuitJson = await getCircuitJsonForCheck({
|
|
241037
|
+
filePath: resolvedInputFilePath,
|
|
241038
|
+
platformConfig: {
|
|
241039
|
+
pcbDisabled: false,
|
|
241040
|
+
routingDisabled: false
|
|
241041
|
+
},
|
|
241042
|
+
allowPrebuiltCircuitJson: true
|
|
241043
|
+
});
|
|
241044
|
+
const analysis = analyzeCircuitJsonTraceLength(circuitJson, {
|
|
241045
|
+
targetPinOrNet: pinOrNetRef
|
|
241046
|
+
});
|
|
241047
|
+
return analysis.toString();
|
|
241048
|
+
};
|
|
241049
|
+
var registerCheckTraceLength = (program2) => {
|
|
241050
|
+
program2.commands.find((c3) => c3.name() === "check").command("trace-length").description("Analyze trace length for a pin or net").argument("<pinOrNetRef>", "Pin or net target to analyze").argument("[file]", "Path to the entry file").action(async (pinOrNetRef, file) => {
|
|
241051
|
+
try {
|
|
241052
|
+
const output = await checkTraceLength(pinOrNetRef, file);
|
|
241053
|
+
console.log(output);
|
|
241054
|
+
} catch (error) {
|
|
241055
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
241056
|
+
process.exit(1);
|
|
241057
|
+
}
|
|
241058
|
+
});
|
|
241059
|
+
};
|
|
241060
|
+
|
|
239668
241061
|
// cli/clone/register.ts
|
|
239669
241062
|
import * as fs43 from "node:fs";
|
|
239670
241063
|
import * as path45 from "node:path";
|
|
@@ -270695,7 +272088,7 @@ var registerSetup = (program3) => {
|
|
|
270695
272088
|
};
|
|
270696
272089
|
|
|
270697
272090
|
// lib/shared/result-to-table.ts
|
|
270698
|
-
var
|
|
272091
|
+
var formatNumber5 = (n3) => {
|
|
270699
272092
|
return n3.toExponential(6);
|
|
270700
272093
|
};
|
|
270701
272094
|
function formatRows(rows) {
|
|
@@ -270727,7 +272120,7 @@ var resultToTable = (result) => {
|
|
|
270727
272120
|
for (let i2 = 0;i2 < result.numPoints; i2++) {
|
|
270728
272121
|
const row = [
|
|
270729
272122
|
i2.toString(),
|
|
270730
|
-
...uniqueData.map((d3) =>
|
|
272123
|
+
...uniqueData.map((d3) => formatNumber5(d3.values[i2]))
|
|
270731
272124
|
];
|
|
270732
272125
|
dataRows2.push(row);
|
|
270733
272126
|
}
|
|
@@ -270742,8 +272135,8 @@ var resultToTable = (result) => {
|
|
|
270742
272135
|
const row = [
|
|
270743
272136
|
i2.toString(),
|
|
270744
272137
|
...uniqueData.flatMap((d3) => [
|
|
270745
|
-
|
|
270746
|
-
|
|
272138
|
+
formatNumber5(d3.values[i2].real),
|
|
272139
|
+
formatNumber5(d3.values[i2].img)
|
|
270747
272140
|
])
|
|
270748
272141
|
];
|
|
270749
272142
|
dataRows.push(row);
|
|
@@ -274159,6 +275552,7 @@ registerCheckPinSpecification(program2);
|
|
|
274159
275552
|
registerCheckPlacement(program2);
|
|
274160
275553
|
registerCheckRoutingDifficulty(program2);
|
|
274161
275554
|
registerCheckRouting(program2);
|
|
275555
|
+
registerCheckTraceLength(program2);
|
|
274162
275556
|
registerRegistry(program2);
|
|
274163
275557
|
registerRegistryPackages(program2);
|
|
274164
275558
|
registerRegistryPackagesCreate(program2);
|