@tscircuit/cli 0.1.1739 → 0.1.1740
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 +15 -9
- package/dist/cli/main.js +659 -283
- package/dist/cli/snapshot/snapshot.worker.js +15 -9
- package/dist/lib/index.js +8 -7
- package/package.json +8 -7
package/dist/cli/main.js
CHANGED
|
@@ -39814,7 +39814,7 @@ var init_chunk_4ISXDEM5 = __esm(() => {
|
|
|
39814
39814
|
var exports_dist_3YR26ALR = {};
|
|
39815
39815
|
__export2(exports_dist_3YR26ALR, {
|
|
39816
39816
|
isSimulationVoltageProbe: () => isSimulationVoltageProbe,
|
|
39817
|
-
isSimulationTransientVoltageGraph: () =>
|
|
39817
|
+
isSimulationTransientVoltageGraph: () => isSimulationTransientVoltageGraph,
|
|
39818
39818
|
isSimulationExperiment: () => isSimulationExperiment2,
|
|
39819
39819
|
getSoftwareUsedString: () => getSoftwareUsedString,
|
|
39820
39820
|
createSvgObjectsForSchComponentPortHovers: () => createSvgObjectsForSchComponentPortHovers,
|
|
@@ -49688,7 +49688,7 @@ function convertCircuitJsonToSchematicSvg3(circuitJson, options) {
|
|
|
49688
49688
|
};
|
|
49689
49689
|
return stringify(svgObject);
|
|
49690
49690
|
}
|
|
49691
|
-
function
|
|
49691
|
+
function isSimulationTransientVoltageGraph(value) {
|
|
49692
49692
|
return value?.type === "simulation_transient_voltage_graph";
|
|
49693
49693
|
}
|
|
49694
49694
|
function isSimulationExperiment2(value) {
|
|
@@ -49707,7 +49707,7 @@ function convertCircuitJsonToSimulationGraphSvg2({
|
|
|
49707
49707
|
}) {
|
|
49708
49708
|
const selectedIds = simulation_transient_voltage_graph_ids ? new Set(simulation_transient_voltage_graph_ids) : null;
|
|
49709
49709
|
const experiment = circuitJson.find((element) => isSimulationExperiment2(element) && element.simulation_experiment_id === simulation_experiment_id);
|
|
49710
|
-
const graphs = circuitJson.filter((element) =>
|
|
49710
|
+
const graphs = circuitJson.filter((element) => isSimulationTransientVoltageGraph(element) && element.simulation_experiment_id === simulation_experiment_id && (!selectedIds || selectedIds.has(element.simulation_transient_voltage_graph_id)));
|
|
49711
49711
|
if (graphs.length === 0) {
|
|
49712
49712
|
throw new Error(`No simulation_transient_voltage_graph elements found for simulation_experiment_id "${simulation_experiment_id}"`);
|
|
49713
49713
|
}
|
|
@@ -50249,7 +50249,7 @@ function convertCircuitJsonToSchematicSimulationSvg2({
|
|
|
50249
50249
|
graphAboveSchematic = false,
|
|
50250
50250
|
showErrorsInTextOverlay
|
|
50251
50251
|
}) {
|
|
50252
|
-
const schematicElements = circuitJson.filter((element) => !isSimulationExperiment2(element) && !
|
|
50252
|
+
const schematicElements = circuitJson.filter((element) => !isSimulationExperiment2(element) && !isSimulationTransientVoltageGraph(element));
|
|
50253
50253
|
const clampedRatio = clamp01(schematicHeightRatio);
|
|
50254
50254
|
const rawSchematicHeight = Math.max(1, height * clampedRatio);
|
|
50255
50255
|
const rawSimulationHeight = Math.max(1, height - rawSchematicHeight);
|
|
@@ -118506,7 +118506,7 @@ var import_perfect_cli = __toESM2(require_dist2(), 1);
|
|
|
118506
118506
|
// lib/getVersion.ts
|
|
118507
118507
|
import { createRequire as createRequire2 } from "node:module";
|
|
118508
118508
|
// package.json
|
|
118509
|
-
var version = "0.1.
|
|
118509
|
+
var version = "0.1.1739";
|
|
118510
118510
|
var package_default = {
|
|
118511
118511
|
name: "@tscircuit/cli",
|
|
118512
118512
|
version,
|
|
@@ -118529,7 +118529,7 @@ var package_default = {
|
|
|
118529
118529
|
"@tscircuit/image-utils": "^0.0.8",
|
|
118530
118530
|
"@tscircuit/krt-wasm": "^0.1.6",
|
|
118531
118531
|
"@tscircuit/math-utils": "0.0.36",
|
|
118532
|
-
"@tscircuit/props": "^0.0.
|
|
118532
|
+
"@tscircuit/props": "^0.0.592",
|
|
118533
118533
|
"@tscircuit/runframe": "^0.0.2274",
|
|
118534
118534
|
"@tscircuit/schematic-match-adapt": "^0.0.22",
|
|
118535
118535
|
"@tscircuit/solver-utils": "0.0.16",
|
|
@@ -118543,21 +118543,21 @@ var package_default = {
|
|
|
118543
118543
|
"@types/semver": "^7.5.8",
|
|
118544
118544
|
"bun-match-svg": "^0.0.12",
|
|
118545
118545
|
chokidar: "4.0.1",
|
|
118546
|
-
"circuit-json": "^0.0.
|
|
118546
|
+
"circuit-json": "^0.0.455",
|
|
118547
118547
|
"circuit-json-to-3d-png": "^0.0.6",
|
|
118548
118548
|
"circuit-json-to-bom-csv": "^0.0.7",
|
|
118549
118549
|
"circuit-json-to-connectivity-map": "^0.0.25",
|
|
118550
|
-
"circuit-json-to-footprinter": "^0.0.
|
|
118550
|
+
"circuit-json-to-footprinter": "^0.0.15",
|
|
118551
118551
|
"circuit-json-to-gerber": "^0.0.83",
|
|
118552
118552
|
"circuit-json-to-kicad": "0.0.157",
|
|
118553
118553
|
"circuit-json-to-pnp-csv": "^0.0.7",
|
|
118554
118554
|
"circuit-json-to-readable-netlist": "^0.0.15",
|
|
118555
|
-
"circuit-json-to-spice": "^0.0.
|
|
118555
|
+
"circuit-json-to-spice": "^0.0.45",
|
|
118556
118556
|
"circuit-json-to-step": "^0.0.38",
|
|
118557
118557
|
"circuit-json-to-tscircuit": "^0.0.42",
|
|
118558
118558
|
"circuit-json-trace-length-analysis": "github:tscircuit/circuit-json-trace-length-analysis#2b44792a40df0ca83b6bfb6ac95ed5e35e7168b8",
|
|
118559
118559
|
"circuit-to-canvas": "^0.0.111",
|
|
118560
|
-
"circuit-to-svg": "^0.0.
|
|
118560
|
+
"circuit-to-svg": "^0.0.393",
|
|
118561
118561
|
commander: "^14.0.0",
|
|
118562
118562
|
conf: "^13.1.0",
|
|
118563
118563
|
configstore: "^7.0.0",
|
|
@@ -118586,9 +118586,10 @@ var package_default = {
|
|
|
118586
118586
|
prompts: "^2.4.2",
|
|
118587
118587
|
redaxios: "^0.5.1",
|
|
118588
118588
|
semver: "^7.6.3",
|
|
118589
|
+
spicets: "^0.0.4",
|
|
118589
118590
|
stepts: "^0.0.3",
|
|
118590
118591
|
tempy: "^3.1.0",
|
|
118591
|
-
tscircuit: "0.0.
|
|
118592
|
+
tscircuit: "0.0.2142-libonly",
|
|
118592
118593
|
tsx: "^4.7.1",
|
|
118593
118594
|
"typed-ky": "^0.0.4",
|
|
118594
118595
|
zod: "^3.23.8"
|
|
@@ -133667,20 +133668,26 @@ async function loadLocalStepModelFsMap(circuitJson) {
|
|
|
133667
133668
|
import {
|
|
133668
133669
|
convertCircuitJsonToSchematicSimulationSvg,
|
|
133669
133670
|
convertCircuitJsonToSimulationGraphSvg,
|
|
133670
|
-
isSimulationExperiment
|
|
133671
|
-
isSimulationTransientVoltageGraph
|
|
133671
|
+
isSimulationExperiment
|
|
133672
133672
|
} from "circuit-to-svg";
|
|
133673
|
-
var
|
|
133673
|
+
var SIMULATION_ANALYSIS_RESULT_TYPES = new Set([
|
|
133674
|
+
"simulation_transient_voltage_graph",
|
|
133675
|
+
"simulation_transient_current_graph",
|
|
133676
|
+
"simulation_dc_operating_point_voltage",
|
|
133677
|
+
"simulation_dc_operating_point_current",
|
|
133678
|
+
"simulation_dc_sweep_voltage_graph",
|
|
133679
|
+
"simulation_dc_sweep_current_graph",
|
|
133680
|
+
"simulation_ac_sweep_voltage_graph",
|
|
133681
|
+
"simulation_ac_sweep_current_graph"
|
|
133682
|
+
]);
|
|
133683
|
+
var isSimulationAnalysisResult = (element) => SIMULATION_ANALYSIS_RESULT_TYPES.has(element.type);
|
|
133674
133684
|
var getSimulationSvgInputs = (circuitJson, simulationExperimentId) => {
|
|
133675
|
-
const
|
|
133676
|
-
|
|
133677
|
-
if (simulationTransientCurrentGraphIds.length === 0 && simulationTransientVoltageGraphIds.length === 0) {
|
|
133685
|
+
const hasAnalysisResult = circuitJson.some((element) => isSimulationAnalysisResult(element) && element.simulation_experiment_id === simulationExperimentId);
|
|
133686
|
+
if (!hasAnalysisResult) {
|
|
133678
133687
|
return;
|
|
133679
133688
|
}
|
|
133680
133689
|
return {
|
|
133681
|
-
simulation_experiment_id: simulationExperimentId
|
|
133682
|
-
simulation_transient_current_graph_ids: simulationTransientCurrentGraphIds,
|
|
133683
|
-
simulation_transient_voltage_graph_ids: simulationTransientVoltageGraphIds
|
|
133690
|
+
simulation_experiment_id: simulationExperimentId
|
|
133684
133691
|
};
|
|
133685
133692
|
};
|
|
133686
133693
|
var toFileNameSuffix = (value) => value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -286063,140 +286070,69 @@ var convertKicadFootprintToTsx = async ({
|
|
|
286063
286070
|
console.log(kleur_default.green(`Converted ${outputPath}`));
|
|
286064
286071
|
};
|
|
286065
286072
|
|
|
286066
|
-
// node_modules/circuit-json-to-footprinter/dist/
|
|
286067
|
-
import {
|
|
286068
|
-
|
|
286069
|
-
|
|
286070
|
-
|
|
286071
|
-
|
|
286072
|
-
|
|
286073
|
-
|
|
286074
|
-
|
|
286075
|
-
|
|
286073
|
+
// node_modules/circuit-json-to-footprinter/dist/chunk-HCIYAG7H.js
|
|
286074
|
+
import {
|
|
286075
|
+
getBoundsFromPoints as getBoundsFromPoints4
|
|
286076
|
+
} from "@tscircuit/math-utils";
|
|
286077
|
+
import { getBoundsCenter, isPointInsidePolygon as isPointInsidePolygon3 } from "@tscircuit/math-utils";
|
|
286078
|
+
var toRadians2 = (degrees) => degrees * Math.PI / 180;
|
|
286079
|
+
var rotatePoint4 = (x4, y4, radians) => ({
|
|
286080
|
+
x: x4 * Math.cos(radians) - y4 * Math.sin(radians),
|
|
286081
|
+
y: x4 * Math.sin(radians) + y4 * Math.cos(radians)
|
|
286082
|
+
});
|
|
286083
|
+
var getSizedBounds = (points) => {
|
|
286084
|
+
const bounds = getBoundsFromPoints4([...points]);
|
|
286085
|
+
if (!bounds) {
|
|
286086
|
+
throw new Error("Cannot calculate bounds for an empty point list");
|
|
286076
286087
|
}
|
|
286077
|
-
return
|
|
286078
|
-
|
|
286079
|
-
|
|
286080
|
-
|
|
286081
|
-
|
|
286082
|
-
if (pinMatch)
|
|
286083
|
-
return `pin${pinMatch[1]}`;
|
|
286084
|
-
const numericMatch = trimmed.match(/^(\d+)$/);
|
|
286085
|
-
return numericMatch ? `pin${numericMatch[1]}` : trimmed;
|
|
286088
|
+
return {
|
|
286089
|
+
...bounds,
|
|
286090
|
+
height: bounds.maxY - bounds.minY,
|
|
286091
|
+
width: bounds.maxX - bounds.minX
|
|
286092
|
+
};
|
|
286086
286093
|
};
|
|
286087
|
-
var
|
|
286088
|
-
|
|
286089
|
-
|
|
286090
|
-
|
|
286091
|
-
|
|
286092
|
-
|
|
286093
|
-
if (Math.abs(width - height) < 0.00001 && normalized === "round") {
|
|
286094
|
-
return "circle";
|
|
286094
|
+
var getPolygonArea = (points) => {
|
|
286095
|
+
let doubledArea = 0;
|
|
286096
|
+
for (let index = 0;index < points.length; index += 1) {
|
|
286097
|
+
const current2 = points[index];
|
|
286098
|
+
const next = points[(index + 1) % points.length];
|
|
286099
|
+
doubledArea += current2.x * next.y - next.x * current2.y;
|
|
286095
286100
|
}
|
|
286096
|
-
return
|
|
286097
|
-
};
|
|
286098
|
-
var getCornerRadius = (element, width, height) => {
|
|
286099
|
-
const radius = toNumber4(element.corner_radius ?? element.cornerRadius ?? element.rect_border_radius);
|
|
286100
|
-
return radius > 0 ? Math.min(radius, width / 2, height / 2) : undefined;
|
|
286101
|
+
return Math.abs(doubledArea) / 2;
|
|
286101
286102
|
};
|
|
286102
|
-
var
|
|
286103
|
-
|
|
286104
|
-
|
|
286105
|
-
if (element.type === "pcb_smtpad") {
|
|
286106
|
-
const width = toNumber4(element.width);
|
|
286107
|
-
const height = toNumber4(element.height);
|
|
286108
|
-
return [
|
|
286109
|
-
{
|
|
286110
|
-
cornerRadius: getCornerRadius(element, width, height),
|
|
286111
|
-
height,
|
|
286112
|
-
id: String(element.pcb_smtpad_id ?? `pcb_smtpad_${index + 1}`),
|
|
286113
|
-
kind: "smt",
|
|
286114
|
-
layer: String(element.layer ?? "top"),
|
|
286115
|
-
portHints: Array.isArray(element.port_hints) ? element.port_hints.map((hint) => normalizePortHint(String(hint))) : [],
|
|
286116
|
-
rotation: toNumber4(element.rotation),
|
|
286117
|
-
shape: normalizeShape(element.shape, width, height),
|
|
286118
|
-
width,
|
|
286119
|
-
x: toNumber4(element.x),
|
|
286120
|
-
y: toNumber4(element.y)
|
|
286121
|
-
}
|
|
286122
|
-
];
|
|
286123
|
-
}
|
|
286124
|
-
if (element.type === "pcb_plated_hole") {
|
|
286125
|
-
const width = toNumber4(element.width ?? element.outer_diameter ?? element.outerDiameter, 0.6);
|
|
286126
|
-
const height = toNumber4(element.height, width);
|
|
286127
|
-
return [
|
|
286128
|
-
{
|
|
286129
|
-
cornerRadius: getCornerRadius(element, width, height),
|
|
286130
|
-
height,
|
|
286131
|
-
id: String(element.pcb_plated_hole_id ?? `pcb_plated_hole_${index + 1}`),
|
|
286132
|
-
kind: "plated-hole",
|
|
286133
|
-
layer: Array.isArray(element.layers) ? String(element.layers[0] ?? "top") : "top",
|
|
286134
|
-
portHints: Array.isArray(element.port_hints) ? element.port_hints.map((hint) => normalizePortHint(String(hint))) : [],
|
|
286135
|
-
rotation: toNumber4(element.rotation),
|
|
286136
|
-
shape: normalizeShape(element.shape ?? "circle", width, height),
|
|
286137
|
-
width,
|
|
286138
|
-
x: toNumber4(element.x),
|
|
286139
|
-
y: toNumber4(element.y)
|
|
286140
|
-
}
|
|
286141
|
-
];
|
|
286142
|
-
}
|
|
286143
|
-
return [];
|
|
286144
|
-
});
|
|
286145
|
-
if (pads.length === 0) {
|
|
286146
|
-
throw new Error("Circuit JSON must contain at least one PCB SMT pad or plated hole");
|
|
286103
|
+
var getPolygonWorldPoints = (shape) => {
|
|
286104
|
+
if (shape.shape !== "polygon" || !shape.points) {
|
|
286105
|
+
throw new Error("Polygon preview shapes require points");
|
|
286147
286106
|
}
|
|
286148
|
-
|
|
286149
|
-
|
|
286150
|
-
|
|
286151
|
-
|
|
286152
|
-
title: options.title ?? "Circuit JSON"
|
|
286153
|
-
};
|
|
286154
|
-
};
|
|
286155
|
-
var footprinterStringToPreview = (footprinterString) => {
|
|
286156
|
-
const normalized = footprinterString.trim();
|
|
286157
|
-
if (!normalized)
|
|
286158
|
-
throw new Error("Footprinter string is required");
|
|
286159
|
-
return circuitJsonToPreview(fp3.string(normalized).circuitJson(), {
|
|
286160
|
-
subtitle: "Generated by @tscircuit/footprinter",
|
|
286161
|
-
title: normalized
|
|
286107
|
+
const radians = toRadians2(shape.rotation);
|
|
286108
|
+
return shape.points.map((point9) => {
|
|
286109
|
+
const rotated = rotatePoint4(point9.x, point9.y, radians);
|
|
286110
|
+
return { x: rotated.x + shape.x, y: rotated.y + shape.y };
|
|
286162
286111
|
});
|
|
286163
286112
|
};
|
|
286164
|
-
var
|
|
286165
|
-
|
|
286166
|
-
|
|
286167
|
-
|
|
286168
|
-
|
|
286169
|
-
|
|
286170
|
-
|
|
286171
|
-
const halfWidth =
|
|
286172
|
-
const halfHeight =
|
|
286173
|
-
const radians = toRadians2(
|
|
286113
|
+
var getShapeBounds = (shape) => {
|
|
286114
|
+
if (shape.shape === "polygon") {
|
|
286115
|
+
if (!shape.points || shape.points.length < 3) {
|
|
286116
|
+
throw new Error("Polygon preview shapes require at least three points");
|
|
286117
|
+
}
|
|
286118
|
+
return getSizedBounds(getPolygonWorldPoints(shape));
|
|
286119
|
+
}
|
|
286120
|
+
const halfWidth = shape.width / 2;
|
|
286121
|
+
const halfHeight = shape.height / 2;
|
|
286122
|
+
const radians = toRadians2(shape.rotation);
|
|
286174
286123
|
const corners = [
|
|
286175
286124
|
rotatePoint4(-halfWidth, -halfHeight, radians),
|
|
286176
286125
|
rotatePoint4(halfWidth, -halfHeight, radians),
|
|
286177
286126
|
rotatePoint4(halfWidth, halfHeight, radians),
|
|
286178
286127
|
rotatePoint4(-halfWidth, halfHeight, radians)
|
|
286179
286128
|
].map((corner) => ({
|
|
286180
|
-
x: corner.x +
|
|
286181
|
-
y: corner.y +
|
|
286129
|
+
x: corner.x + shape.x,
|
|
286130
|
+
y: corner.y + shape.y
|
|
286182
286131
|
}));
|
|
286183
|
-
|
|
286184
|
-
const ys3 = corners.map((corner) => corner.y);
|
|
286185
|
-
const minX = Math.min(...xs3);
|
|
286186
|
-
const maxX = Math.max(...xs3);
|
|
286187
|
-
const minY = Math.min(...ys3);
|
|
286188
|
-
const maxY = Math.max(...ys3);
|
|
286189
|
-
return {
|
|
286190
|
-
height: maxY - minY,
|
|
286191
|
-
maxX,
|
|
286192
|
-
maxY,
|
|
286193
|
-
minX,
|
|
286194
|
-
minY,
|
|
286195
|
-
width: maxX - minX
|
|
286196
|
-
};
|
|
286132
|
+
return getSizedBounds(corners);
|
|
286197
286133
|
};
|
|
286198
|
-
var getFootprintBounds = (
|
|
286199
|
-
if (
|
|
286134
|
+
var getFootprintBounds = (shapes) => {
|
|
286135
|
+
if (shapes.length === 0) {
|
|
286200
286136
|
return {
|
|
286201
286137
|
height: 1,
|
|
286202
286138
|
maxX: 0.5,
|
|
@@ -286206,20 +286142,13 @@ var getFootprintBounds = (pads) => {
|
|
|
286206
286142
|
width: 1
|
|
286207
286143
|
};
|
|
286208
286144
|
}
|
|
286209
|
-
const bounds =
|
|
286210
|
-
|
|
286211
|
-
|
|
286212
|
-
|
|
286213
|
-
|
|
286214
|
-
return {
|
|
286215
|
-
height: maxY - minY,
|
|
286216
|
-
maxX,
|
|
286217
|
-
maxY,
|
|
286218
|
-
minX,
|
|
286219
|
-
minY,
|
|
286220
|
-
width: maxX - minX
|
|
286221
|
-
};
|
|
286145
|
+
const bounds = shapes.map(getShapeBounds);
|
|
286146
|
+
return getSizedBounds(bounds.flatMap((bound) => [
|
|
286147
|
+
{ x: bound.minX, y: bound.minY },
|
|
286148
|
+
{ x: bound.maxX, y: bound.maxY }
|
|
286149
|
+
]));
|
|
286222
286150
|
};
|
|
286151
|
+
var DEFAULT_GRID_SIZE = 320;
|
|
286223
286152
|
var addPadding = (bounds) => {
|
|
286224
286153
|
const padX = Math.max(bounds.width * 0.18, 0.65);
|
|
286225
286154
|
const padY = Math.max(bounds.height * 0.18, 0.65);
|
|
@@ -286242,21 +286171,40 @@ var translateFootprint = (footprint, deltaX, deltaY) => ({
|
|
|
286242
286171
|
});
|
|
286243
286172
|
var centerFootprint = (footprint) => {
|
|
286244
286173
|
const bounds = getFootprintBounds(footprint.pads);
|
|
286245
|
-
const
|
|
286246
|
-
|
|
286247
|
-
|
|
286248
|
-
|
|
286249
|
-
|
|
286250
|
-
|
|
286251
|
-
|
|
286252
|
-
|
|
286253
|
-
|
|
286254
|
-
|
|
286255
|
-
|
|
286174
|
+
const center2 = getBoundsCenter(bounds);
|
|
286175
|
+
return translateFootprint(footprint, -center2.x, -center2.y);
|
|
286176
|
+
};
|
|
286177
|
+
var getHoleShapes = (pads) => pads.flatMap((pad2) => {
|
|
286178
|
+
if (!pad2.hole)
|
|
286179
|
+
return [];
|
|
286180
|
+
return [
|
|
286181
|
+
{
|
|
286182
|
+
height: pad2.hole.height,
|
|
286183
|
+
rotation: pad2.hole.rotation,
|
|
286184
|
+
shape: pad2.hole.shape,
|
|
286185
|
+
width: pad2.hole.width,
|
|
286186
|
+
x: pad2.x + pad2.hole.offsetX,
|
|
286187
|
+
y: pad2.y + pad2.hole.offsetY
|
|
286188
|
+
}
|
|
286189
|
+
];
|
|
286190
|
+
});
|
|
286191
|
+
var pointInShape = (x4, y4, shape) => {
|
|
286192
|
+
const dx3 = x4 - shape.x;
|
|
286193
|
+
const dy3 = y4 - shape.y;
|
|
286194
|
+
const local = rotatePoint4(dx3, dy3, -toRadians2(shape.rotation));
|
|
286195
|
+
const halfWidth = shape.width / 2;
|
|
286196
|
+
const halfHeight = shape.height / 2;
|
|
286197
|
+
if (shape.shape === "polygon") {
|
|
286198
|
+
if (!shape.points || shape.points.length < 3) {
|
|
286199
|
+
throw new Error("Polygon preview shapes require at least three points");
|
|
286200
|
+
}
|
|
286201
|
+
return isPointInsidePolygon3(local, shape.points);
|
|
286202
|
+
}
|
|
286203
|
+
if (shape.shape === "circle") {
|
|
286256
286204
|
return Math.hypot(local.x, local.y) <= Math.min(halfWidth, halfHeight);
|
|
286257
286205
|
}
|
|
286258
|
-
if (
|
|
286259
|
-
const cornerRadius = Math.max(0, Math.min(
|
|
286206
|
+
if (shape.shape === "rect") {
|
|
286207
|
+
const cornerRadius = Math.max(0, Math.min(shape.cornerRadius ?? 0, halfWidth, halfHeight));
|
|
286260
286208
|
if (cornerRadius === 0) {
|
|
286261
286209
|
return Math.abs(local.x) <= halfWidth && Math.abs(local.y) <= halfHeight;
|
|
286262
286210
|
}
|
|
@@ -286272,7 +286220,7 @@ var pointInPad = (x4, y4, pad2) => {
|
|
|
286272
286220
|
const cornerDy = absY - innerHalfHeight;
|
|
286273
286221
|
return cornerDx >= 0 && cornerDy >= 0 && cornerDx * cornerDx + cornerDy * cornerDy <= cornerRadius * cornerRadius;
|
|
286274
286222
|
}
|
|
286275
|
-
if (
|
|
286223
|
+
if (shape.width >= shape.height) {
|
|
286276
286224
|
const capsuleLength2 = halfWidth - halfHeight;
|
|
286277
286225
|
if (Math.abs(local.x) <= capsuleLength2 && Math.abs(local.y) <= halfHeight) {
|
|
286278
286226
|
return true;
|
|
@@ -286299,12 +286247,18 @@ var mergeBounds3 = (left, right) => {
|
|
|
286299
286247
|
width: maxX - minX
|
|
286300
286248
|
};
|
|
286301
286249
|
};
|
|
286302
|
-
var
|
|
286303
|
-
|
|
286304
|
-
|
|
286305
|
-
|
|
286250
|
+
var getComparisonBounds = (left, right) => {
|
|
286251
|
+
if (!left.length)
|
|
286252
|
+
return addPadding(getFootprintBounds(right));
|
|
286253
|
+
if (!right.length)
|
|
286254
|
+
return addPadding(getFootprintBounds(left));
|
|
286255
|
+
return addPadding(mergeBounds3(getFootprintBounds(left), getFootprintBounds(right)));
|
|
286256
|
+
};
|
|
286257
|
+
var rasterizeShapes = (left, right, gridSize, includeOccupancy) => {
|
|
286258
|
+
const bounds = getComparisonBounds(left, right);
|
|
286306
286259
|
const cellWidth = bounds.width / gridSize;
|
|
286307
286260
|
const cellHeight = bounds.height / gridSize;
|
|
286261
|
+
const occupancy = includeOccupancy ? new Uint8Array(gridSize * gridSize) : undefined;
|
|
286308
286262
|
let leftCount = 0;
|
|
286309
286263
|
let rightCount = 0;
|
|
286310
286264
|
let overlapCount = 0;
|
|
@@ -286312,24 +286266,230 @@ var summarizeCopperComparison = (left, right, gridSize = DEFAULT_GRID_SIZE) => {
|
|
|
286312
286266
|
const sampleY = bounds.maxY - (row + 0.5) * cellHeight;
|
|
286313
286267
|
for (let column = 0;column < gridSize; column += 1) {
|
|
286314
286268
|
const sampleX = bounds.minX + (column + 0.5) * cellWidth;
|
|
286315
|
-
const inLeft =
|
|
286316
|
-
const inRight =
|
|
286269
|
+
const inLeft = left.some((shape) => pointInShape(sampleX, sampleY, shape));
|
|
286270
|
+
const inRight = right.some((shape) => pointInShape(sampleX, sampleY, shape));
|
|
286317
286271
|
if (inLeft)
|
|
286318
286272
|
leftCount += 1;
|
|
286319
286273
|
if (inRight)
|
|
286320
286274
|
rightCount += 1;
|
|
286321
286275
|
if (inLeft && inRight)
|
|
286322
286276
|
overlapCount += 1;
|
|
286277
|
+
if (occupancy) {
|
|
286278
|
+
const index = row * gridSize + column;
|
|
286279
|
+
occupancy[index] = inLeft && inRight ? 3 : inLeft ? 1 : inRight ? 2 : 0;
|
|
286280
|
+
}
|
|
286323
286281
|
}
|
|
286324
286282
|
}
|
|
286325
286283
|
const unionCount = leftCount + rightCount - overlapCount;
|
|
286326
|
-
const cellArea = cellWidth * cellHeight;
|
|
286327
|
-
const intersectionArea2 = overlapCount * cellArea;
|
|
286328
|
-
const unionArea = unionCount * cellArea;
|
|
286329
286284
|
return {
|
|
286330
|
-
|
|
286285
|
+
coverageLeft: leftCount === 0 ? 0 : overlapCount / leftCount,
|
|
286286
|
+
coverageRight: rightCount === 0 ? 0 : overlapCount / rightCount,
|
|
286287
|
+
iou: unionCount === 0 ? 0 : overlapCount / unionCount,
|
|
286288
|
+
leftOnlyRatio: unionCount === 0 ? 0 : Math.max(leftCount - overlapCount, 0) / unionCount,
|
|
286289
|
+
occupancy,
|
|
286290
|
+
rightOnlyRatio: unionCount === 0 ? 0 : Math.max(rightCount - overlapCount, 0) / unionCount
|
|
286291
|
+
};
|
|
286292
|
+
};
|
|
286293
|
+
var compareNormalizedFootprints = (left, right, gridSize, includeOccupancy) => {
|
|
286294
|
+
const normalizedLeft = centerFootprint(left);
|
|
286295
|
+
const normalizedRight = centerFootprint(right);
|
|
286296
|
+
const comparison = rasterizeShapes(normalizedLeft.pads, normalizedRight.pads, gridSize, includeOccupancy);
|
|
286297
|
+
return { comparison, normalizedLeft, normalizedRight };
|
|
286298
|
+
};
|
|
286299
|
+
var summarizeCopperComparison = (left, right, gridSize = DEFAULT_GRID_SIZE) => {
|
|
286300
|
+
const { comparison, normalizedLeft, normalizedRight } = compareNormalizedFootprints(left, right, gridSize, false);
|
|
286301
|
+
const leftHoles = getHoleShapes(normalizedLeft.pads);
|
|
286302
|
+
const rightHoles = getHoleShapes(normalizedRight.pads);
|
|
286303
|
+
const holeIntersectionOverUnion = leftHoles.length === 0 && rightHoles.length === 0 ? 1 : rasterizeShapes(leftHoles, rightHoles, gridSize, false).iou;
|
|
286304
|
+
return {
|
|
286305
|
+
copperIntersectionOverUnion: comparison.iou,
|
|
286306
|
+
holeIntersectionOverUnion
|
|
286307
|
+
};
|
|
286308
|
+
};
|
|
286309
|
+
|
|
286310
|
+
// node_modules/circuit-json-to-footprinter/dist/index.js
|
|
286311
|
+
import { fp as fp3 } from "@tscircuit/footprinter";
|
|
286312
|
+
import {
|
|
286313
|
+
getBoundsCenter as getBoundsCenter2,
|
|
286314
|
+
getBoundsFromPoints as getBoundsFromPoints5
|
|
286315
|
+
} from "@tscircuit/math-utils";
|
|
286316
|
+
import { getFootprintNames as getFootprintNames2, getFootprintSizes } from "@tscircuit/footprinter";
|
|
286317
|
+
var toNumber4 = (value, fallback = 0) => {
|
|
286318
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
286319
|
+
return value;
|
|
286320
|
+
if (typeof value === "string") {
|
|
286321
|
+
const parsed = Number.parseFloat(value);
|
|
286322
|
+
if (Number.isFinite(parsed))
|
|
286323
|
+
return parsed;
|
|
286324
|
+
}
|
|
286325
|
+
return fallback;
|
|
286326
|
+
};
|
|
286327
|
+
var normalizePortHint = (hint) => {
|
|
286328
|
+
const trimmed = hint.trim();
|
|
286329
|
+
const pinMatch = trimmed.match(/^pin(\d+)$/i);
|
|
286330
|
+
if (pinMatch)
|
|
286331
|
+
return `pin${pinMatch[1]}`;
|
|
286332
|
+
const numericMatch = trimmed.match(/^(\d+)$/);
|
|
286333
|
+
return numericMatch ? `pin${numericMatch[1]}` : trimmed;
|
|
286334
|
+
};
|
|
286335
|
+
var normalizeShape = (shape, width, height) => {
|
|
286336
|
+
const normalized = typeof shape === "string" ? shape.toLowerCase() : "rect";
|
|
286337
|
+
if (normalized === "circle" || normalized === "ellipse")
|
|
286338
|
+
return "circle";
|
|
286339
|
+
if (normalized === "pill" || normalized === "oval" || normalized === "rotated_pill") {
|
|
286340
|
+
return "pill";
|
|
286341
|
+
}
|
|
286342
|
+
if (Math.abs(width - height) < 0.00001 && normalized === "round") {
|
|
286343
|
+
return "circle";
|
|
286344
|
+
}
|
|
286345
|
+
return "rect";
|
|
286346
|
+
};
|
|
286347
|
+
var getCornerRadius = (element, width, height) => {
|
|
286348
|
+
const radius = toNumber4(element.corner_radius ?? element.cornerRadius ?? element.rect_border_radius);
|
|
286349
|
+
return radius > 0 ? Math.min(radius, width / 2, height / 2) : undefined;
|
|
286350
|
+
};
|
|
286351
|
+
var getPlatedHolePadGeometry = (element) => {
|
|
286352
|
+
const elementShape = String(element.shape ?? "circle").toLowerCase();
|
|
286353
|
+
const hasRectPad = element.pad_shape === "rect" || elementShape.includes("with_rect_pad");
|
|
286354
|
+
const outerDiameter = toNumber4(element.outer_diameter ?? element.outerDiameter, 0.6);
|
|
286355
|
+
const width = hasRectPad ? toNumber4(element.rect_pad_width ?? element.outer_width ?? element.width, outerDiameter) : toNumber4(element.outer_width ?? element.width, outerDiameter);
|
|
286356
|
+
const height = hasRectPad ? toNumber4(element.rect_pad_height ?? element.outer_height ?? element.height, width) : toNumber4(element.outer_height ?? element.height, width);
|
|
286357
|
+
const shape = hasRectPad ? normalizeShape(element.pad_shape ?? "rect", width, height) : normalizeShape(elementShape, width, height);
|
|
286358
|
+
const rotation11 = hasRectPad ? toNumber4(element.rect_ccw_rotation ?? element.ccw_rotation ?? element.rotation) : toNumber4(element.ccw_rotation ?? element.rotation);
|
|
286359
|
+
return { height, rotation: rotation11, shape, width };
|
|
286360
|
+
};
|
|
286361
|
+
var getPlatedHoleGeometry = (element) => {
|
|
286362
|
+
const elementShape = String(element.shape ?? "circle").toLowerCase();
|
|
286363
|
+
const rawHoleShape = String(element.hole_shape ?? (elementShape === "circle" || elementShape === "oval" || elementShape === "pill" ? elementShape : "circle")).toLowerCase();
|
|
286364
|
+
const holeDiameter = toNumber4(element.hole_diameter);
|
|
286365
|
+
const width = toNumber4(element.hole_width, holeDiameter);
|
|
286366
|
+
const height = toNumber4(element.hole_height, width);
|
|
286367
|
+
if (width <= 0 || height <= 0)
|
|
286368
|
+
return;
|
|
286369
|
+
return {
|
|
286370
|
+
height,
|
|
286371
|
+
offsetX: toNumber4(element.hole_offset_x),
|
|
286372
|
+
offsetY: toNumber4(element.hole_offset_y),
|
|
286373
|
+
rotation: toNumber4(element.hole_ccw_rotation ?? (elementShape === "oval" || elementShape === "pill" ? element.ccw_rotation : undefined) ?? element.rotation),
|
|
286374
|
+
shape: normalizeShape(rawHoleShape, width, height),
|
|
286375
|
+
width
|
|
286376
|
+
};
|
|
286377
|
+
};
|
|
286378
|
+
var parsePolygonPoints = (value, elementName) => {
|
|
286379
|
+
if (!Array.isArray(value) || value.length < 3) {
|
|
286380
|
+
throw new Error(`${elementName} must contain at least three points`);
|
|
286381
|
+
}
|
|
286382
|
+
const points = value.map((rawPoint) => {
|
|
286383
|
+
if (!rawPoint || typeof rawPoint !== "object") {
|
|
286384
|
+
throw new Error(`${elementName} points must contain finite x/y values`);
|
|
286385
|
+
}
|
|
286386
|
+
const point9 = rawPoint;
|
|
286387
|
+
const x4 = toNumber4(point9.x, Number.NaN);
|
|
286388
|
+
const y4 = toNumber4(point9.y, Number.NaN);
|
|
286389
|
+
if (!Number.isFinite(x4) || !Number.isFinite(y4)) {
|
|
286390
|
+
throw new Error(`${elementName} points must contain finite x/y values`);
|
|
286391
|
+
}
|
|
286392
|
+
return { x: x4, y: y4 };
|
|
286393
|
+
});
|
|
286394
|
+
if (getPolygonArea(points) <= 0.000000000001) {
|
|
286395
|
+
throw new Error(`${elementName} must enclose a non-zero area`);
|
|
286396
|
+
}
|
|
286397
|
+
return points;
|
|
286398
|
+
};
|
|
286399
|
+
var getPolygonSmtPadGeometry = (element) => {
|
|
286400
|
+
const absolutePoints = parsePolygonPoints(element.points, "Polygon PCB SMT pads");
|
|
286401
|
+
const bounds = getBoundsFromPoints5(absolutePoints);
|
|
286402
|
+
if (!bounds) {
|
|
286403
|
+
throw new Error("Polygon PCB SMT pads must contain at least three points");
|
|
286404
|
+
}
|
|
286405
|
+
const { x: x4, y: y4 } = getBoundsCenter2(bounds);
|
|
286406
|
+
return {
|
|
286407
|
+
height: bounds.maxY - bounds.minY,
|
|
286408
|
+
points: absolutePoints.map((point9) => ({
|
|
286409
|
+
x: point9.x - x4,
|
|
286410
|
+
y: point9.y - y4
|
|
286411
|
+
})),
|
|
286412
|
+
width: bounds.maxX - bounds.minX,
|
|
286413
|
+
x: x4,
|
|
286414
|
+
y: y4
|
|
286415
|
+
};
|
|
286416
|
+
};
|
|
286417
|
+
var getSmtPadMetadata = (element, index) => ({
|
|
286418
|
+
id: String(element.pcb_smtpad_id ?? `pcb_smtpad_${index + 1}`),
|
|
286419
|
+
kind: "smt",
|
|
286420
|
+
layer: String(element.layer ?? "top"),
|
|
286421
|
+
portHints: Array.isArray(element.port_hints) ? element.port_hints.map((hint) => normalizePortHint(String(hint))) : []
|
|
286422
|
+
});
|
|
286423
|
+
var parseSmtPad = (element, index) => {
|
|
286424
|
+
const metadata = getSmtPadMetadata(element, index);
|
|
286425
|
+
if (element.shape === "polygon") {
|
|
286426
|
+
return {
|
|
286427
|
+
...metadata,
|
|
286428
|
+
...getPolygonSmtPadGeometry(element),
|
|
286429
|
+
rotation: 0,
|
|
286430
|
+
shape: "polygon"
|
|
286431
|
+
};
|
|
286432
|
+
}
|
|
286433
|
+
const diameter = toNumber4(element.radius) * 2;
|
|
286434
|
+
const width = toNumber4(element.width, diameter);
|
|
286435
|
+
const height = toNumber4(element.height, width);
|
|
286436
|
+
return {
|
|
286437
|
+
...metadata,
|
|
286438
|
+
cornerRadius: getCornerRadius(element, width, height),
|
|
286439
|
+
height,
|
|
286440
|
+
rotation: toNumber4(element.ccw_rotation ?? element.rotation),
|
|
286441
|
+
shape: normalizeShape(element.shape, width, height),
|
|
286442
|
+
width,
|
|
286443
|
+
x: toNumber4(element.x),
|
|
286444
|
+
y: toNumber4(element.y)
|
|
286445
|
+
};
|
|
286446
|
+
};
|
|
286447
|
+
var circuitJsonToPreview = (circuitJson, options = {}) => {
|
|
286448
|
+
const pads = circuitJson.flatMap((rawElement, index) => {
|
|
286449
|
+
const element = rawElement;
|
|
286450
|
+
if (element.type === "pcb_smtpad") {
|
|
286451
|
+
return [parseSmtPad(element, index)];
|
|
286452
|
+
}
|
|
286453
|
+
if (element.type === "pcb_plated_hole") {
|
|
286454
|
+
const { height, rotation: rotation11, shape, width } = getPlatedHolePadGeometry(element);
|
|
286455
|
+
return [
|
|
286456
|
+
{
|
|
286457
|
+
cornerRadius: getCornerRadius(element, width, height),
|
|
286458
|
+
height,
|
|
286459
|
+
hole: getPlatedHoleGeometry(element),
|
|
286460
|
+
id: String(element.pcb_plated_hole_id ?? `pcb_plated_hole_${index + 1}`),
|
|
286461
|
+
kind: "plated-hole",
|
|
286462
|
+
layer: Array.isArray(element.layers) ? String(element.layers[0] ?? "top") : "top",
|
|
286463
|
+
portHints: Array.isArray(element.port_hints) ? element.port_hints.map((hint) => normalizePortHint(String(hint))) : [],
|
|
286464
|
+
rotation: rotation11,
|
|
286465
|
+
shape,
|
|
286466
|
+
width,
|
|
286467
|
+
x: toNumber4(element.x),
|
|
286468
|
+
y: toNumber4(element.y)
|
|
286469
|
+
}
|
|
286470
|
+
];
|
|
286471
|
+
}
|
|
286472
|
+
return [];
|
|
286473
|
+
});
|
|
286474
|
+
if (pads.length === 0) {
|
|
286475
|
+
throw new Error("Circuit JSON must contain at least one PCB SMT pad or plated hole");
|
|
286476
|
+
}
|
|
286477
|
+
return {
|
|
286478
|
+
pads,
|
|
286479
|
+
sourceHints: options.sourceHints,
|
|
286480
|
+
subtitle: options.subtitle ?? "Circuit JSON footprint",
|
|
286481
|
+
title: options.title ?? "Circuit JSON"
|
|
286331
286482
|
};
|
|
286332
286483
|
};
|
|
286484
|
+
var footprinterStringToPreview = (footprinterString) => {
|
|
286485
|
+
const normalized = footprinterString.trim();
|
|
286486
|
+
if (!normalized)
|
|
286487
|
+
throw new Error("Footprinter string is required");
|
|
286488
|
+
return circuitJsonToPreview(fp3.string(normalized).circuitJson(), {
|
|
286489
|
+
subtitle: "Generated by @tscircuit/footprinter",
|
|
286490
|
+
title: normalized
|
|
286491
|
+
});
|
|
286492
|
+
};
|
|
286333
286493
|
var SEARCH_GRID_SIZE = 112;
|
|
286334
286494
|
var MAX_OPTIMIZED_SEEDS = 10;
|
|
286335
286495
|
var OPTIMIZATION_STEPS = 16;
|
|
@@ -286345,6 +286505,34 @@ var NUMERIC_PARAMETERS = [
|
|
|
286345
286505
|
"od",
|
|
286346
286506
|
"id"
|
|
286347
286507
|
];
|
|
286508
|
+
var FOOTPRINT_ROTATIONS = [0, 90, 180, 270];
|
|
286509
|
+
var PIN1_LOCATIONS = [
|
|
286510
|
+
["leftside", "top"],
|
|
286511
|
+
["leftside", "bottom"],
|
|
286512
|
+
["rightside", "top"],
|
|
286513
|
+
["rightside", "bottom"],
|
|
286514
|
+
["topside", "left"],
|
|
286515
|
+
["topside", "right"],
|
|
286516
|
+
["bottomside", "left"],
|
|
286517
|
+
["bottomside", "right"]
|
|
286518
|
+
];
|
|
286519
|
+
var getOrientedHeuristics = (seed, analysis) => {
|
|
286520
|
+
const heuristics = seed.family === "lga" ? {
|
|
286521
|
+
...analysis.heuristics,
|
|
286522
|
+
h: analysis.bounds.height,
|
|
286523
|
+
pl: analysis.lgaPadLength,
|
|
286524
|
+
pw: analysis.lgaPadWidth,
|
|
286525
|
+
w: analysis.bounds.width
|
|
286526
|
+
} : analysis.heuristics;
|
|
286527
|
+
if (seed.searchRotation !== 90 && seed.searchRotation !== 270) {
|
|
286528
|
+
return heuristics;
|
|
286529
|
+
}
|
|
286530
|
+
return {
|
|
286531
|
+
...heuristics,
|
|
286532
|
+
h: heuristics.w,
|
|
286533
|
+
w: heuristics.h
|
|
286534
|
+
};
|
|
286535
|
+
};
|
|
286348
286536
|
var clamp5 = (value, min, max2) => Math.min(Math.max(value, min), max2);
|
|
286349
286537
|
var median = (values) => {
|
|
286350
286538
|
if (!values.length)
|
|
@@ -286353,36 +286541,8 @@ var median = (values) => {
|
|
|
286353
286541
|
const middle = Math.floor(sorted.length / 2);
|
|
286354
286542
|
return sorted.length % 2 === 0 ? (sorted[middle - 1] + sorted[middle]) / 2 : sorted[middle];
|
|
286355
286543
|
};
|
|
286356
|
-
var
|
|
286357
|
-
|
|
286358
|
-
const cosine = Math.abs(Math.cos(radians));
|
|
286359
|
-
const sine = Math.abs(Math.sin(radians));
|
|
286360
|
-
const width = pad2.width * cosine + pad2.height * sine;
|
|
286361
|
-
const height = pad2.width * sine + pad2.height * cosine;
|
|
286362
|
-
return {
|
|
286363
|
-
height,
|
|
286364
|
-
maxX: pad2.x + width / 2,
|
|
286365
|
-
maxY: pad2.y + height / 2,
|
|
286366
|
-
minX: pad2.x - width / 2,
|
|
286367
|
-
minY: pad2.y - height / 2,
|
|
286368
|
-
width
|
|
286369
|
-
};
|
|
286370
|
-
};
|
|
286371
|
-
var getBounds3 = (pads) => {
|
|
286372
|
-
const padBounds = pads.map(getPadBounds22);
|
|
286373
|
-
const minX = Math.min(...padBounds.map((bound) => bound.minX));
|
|
286374
|
-
const minY = Math.min(...padBounds.map((bound) => bound.minY));
|
|
286375
|
-
const maxX = Math.max(...padBounds.map((bound) => bound.maxX));
|
|
286376
|
-
const maxY = Math.max(...padBounds.map((bound) => bound.maxY));
|
|
286377
|
-
return {
|
|
286378
|
-
height: maxY - minY,
|
|
286379
|
-
maxX,
|
|
286380
|
-
maxY,
|
|
286381
|
-
minX,
|
|
286382
|
-
minY,
|
|
286383
|
-
width: maxX - minX
|
|
286384
|
-
};
|
|
286385
|
-
};
|
|
286544
|
+
var getPadBounds3 = (pad2) => getFootprintBounds([pad2]);
|
|
286545
|
+
var getBounds3 = (pads) => getFootprintBounds(pads);
|
|
286386
286546
|
var clusterCoordinates = (values, tolerance) => {
|
|
286387
286547
|
const sorted = [...values].sort((left, right) => left - right);
|
|
286388
286548
|
const clusters = [];
|
|
@@ -286412,39 +286572,59 @@ var getPitchEstimate = (pads, tolerance) => {
|
|
|
286412
286572
|
}
|
|
286413
286573
|
if (!differences.length)
|
|
286414
286574
|
return Math.max(tolerance * 2, 1);
|
|
286415
|
-
|
|
286575
|
+
const sortedDifferences = differences.toSorted((left, right) => left - right);
|
|
286576
|
+
return sortedDifferences[Math.floor((sortedDifferences.length - 1) * 0.25)];
|
|
286416
286577
|
};
|
|
286417
286578
|
var analyzeTarget = (target) => {
|
|
286418
286579
|
const bounds = getBounds3(target.pads);
|
|
286419
|
-
const padBounds = target.pads.map(
|
|
286580
|
+
const padBounds = target.pads.map(getPadBounds3);
|
|
286420
286581
|
const medianPadWidth = median(padBounds.map((bound) => bound.width));
|
|
286421
286582
|
const medianPadHeight = median(padBounds.map((bound) => bound.height));
|
|
286422
286583
|
const tolerance = Math.max(Math.min(medianPadWidth, medianPadHeight) * 0.22, 0.015);
|
|
286423
|
-
const xCoordinates = clusterCoordinates(target.pads.map((pad2) => pad2.x), tolerance);
|
|
286424
|
-
const yCoordinates = clusterCoordinates(target.pads.map((pad2) => pad2.y), tolerance);
|
|
286425
286584
|
const centerX = (bounds.minX + bounds.maxX) / 2;
|
|
286426
286585
|
const centerY = (bounds.minY + bounds.maxY) / 2;
|
|
286427
|
-
const edgeToleranceX = Math.max(medianPadWidth * 0.75, bounds.width * 0.08);
|
|
286428
|
-
const edgeToleranceY = Math.max(medianPadHeight * 0.75, bounds.height * 0.08);
|
|
286429
|
-
const sidePads = target.pads.filter((pad2) => Math.abs(pad2.x - (bounds.minX + medianPadWidth / 2)) <= edgeToleranceX || Math.abs(pad2.x - (bounds.maxX - medianPadWidth / 2)) <= edgeToleranceX || Math.abs(pad2.y - (bounds.minY + medianPadHeight / 2)) <= edgeToleranceY || Math.abs(pad2.y - (bounds.maxY - medianPadHeight / 2)) <= edgeToleranceY);
|
|
286430
286586
|
const medianPadArea = median(padBounds.map((bound) => bound.width * bound.height));
|
|
286431
|
-
const
|
|
286432
|
-
const
|
|
286433
|
-
const
|
|
286587
|
+
const thermalPadEntry = target.pads.map((pad2) => ({ bound: getPadBounds3(pad2), pad: pad2 })).filter(({ bound, pad: pad2 }) => Math.abs(pad2.x - centerX) <= medianPadWidth && Math.abs(pad2.y - centerY) <= medianPadHeight && bound.width * bound.height > medianPadArea * 2.5).sort((left, right) => right.bound.width * right.bound.height - left.bound.width * left.bound.height)[0];
|
|
286588
|
+
const topologyPads = thermalPadEntry ? target.pads.filter((pad2) => pad2 !== thermalPadEntry.pad) : target.pads;
|
|
286589
|
+
const topologyBounds = getBounds3(topologyPads);
|
|
286590
|
+
const topologyCenterX = (topologyBounds.minX + topologyBounds.maxX) / 2;
|
|
286591
|
+
const topologyCenterY = (topologyBounds.minY + topologyBounds.maxY) / 2;
|
|
286592
|
+
const xCoordinates = clusterCoordinates(topologyPads.map((pad2) => pad2.x), tolerance);
|
|
286593
|
+
const yCoordinates = clusterCoordinates(topologyPads.map((pad2) => pad2.y), tolerance);
|
|
286594
|
+
const edgeToleranceX = Math.max(medianPadWidth * 0.75, topologyBounds.width * 0.08);
|
|
286595
|
+
const edgeToleranceY = Math.max(medianPadHeight * 0.75, topologyBounds.height * 0.08);
|
|
286596
|
+
const sidePads = topologyPads.filter((pad2) => Math.abs(pad2.x - (topologyBounds.minX + medianPadWidth / 2)) <= edgeToleranceX || Math.abs(pad2.x - (topologyBounds.maxX - medianPadWidth / 2)) <= edgeToleranceX || Math.abs(pad2.y - (topologyBounds.minY + medianPadHeight / 2)) <= edgeToleranceY || Math.abs(pad2.y - (topologyBounds.maxY - medianPadHeight / 2)) <= edgeToleranceY);
|
|
286597
|
+
const minPadCenterX = Math.min(...topologyPads.map((pad2) => pad2.x));
|
|
286598
|
+
const maxPadCenterX = Math.max(...topologyPads.map((pad2) => pad2.x));
|
|
286599
|
+
const minPadCenterY = Math.min(...topologyPads.map((pad2) => pad2.y));
|
|
286600
|
+
const maxPadCenterY = Math.max(...topologyPads.map((pad2) => pad2.y));
|
|
286601
|
+
const topologyPadEntries = topologyPads.map((pad2) => ({
|
|
286602
|
+
bounds: getPadBounds3(pad2),
|
|
286603
|
+
pad: pad2
|
|
286604
|
+
}));
|
|
286605
|
+
const leftRightEdgePads = topologyPadEntries.filter(({ pad: pad2 }) => Math.abs(pad2.x - minPadCenterX) <= tolerance || Math.abs(pad2.x - maxPadCenterX) <= tolerance);
|
|
286606
|
+
const topBottomEdgePads = topologyPadEntries.filter(({ pad: pad2 }) => Math.abs(pad2.y - minPadCenterY) <= tolerance || Math.abs(pad2.y - maxPadCenterY) <= tolerance);
|
|
286607
|
+
const leftSidePadCount = topologyPadEntries.filter(({ bounds: bound, pad: pad2 }) => Math.abs(pad2.x - minPadCenterX) <= edgeToleranceX && bound.width >= bound.height * 0.95).length;
|
|
286608
|
+
const rightSidePadCount = topologyPadEntries.filter(({ bounds: bound, pad: pad2 }) => Math.abs(pad2.x - maxPadCenterX) <= edgeToleranceX && bound.width >= bound.height * 0.95).length;
|
|
286609
|
+
const bottomSidePadCount = topologyPadEntries.filter(({ bounds: bound, pad: pad2 }) => Math.abs(pad2.y - minPadCenterY) <= edgeToleranceY && bound.height >= bound.width * 0.95).length;
|
|
286610
|
+
const topSidePadCount = topologyPadEntries.filter(({ bounds: bound, pad: pad2 }) => Math.abs(pad2.y - maxPadCenterY) <= edgeToleranceY && bound.height >= bound.width * 0.95).length;
|
|
286611
|
+
const gridOccupancy = topologyPads.length / Math.max(xCoordinates.length * yCoordinates.length, 1);
|
|
286612
|
+
const hasPadsOnFourSides = sidePads.filter((pad2) => Math.abs(pad2.x - topologyCenterX) > topologyBounds.width * 0.3).length >= 4 && sidePads.filter((pad2) => Math.abs(pad2.y - topologyCenterY) > topologyBounds.height * 0.3).length >= 4;
|
|
286434
286613
|
let topology = "irregular";
|
|
286435
286614
|
if (xCoordinates.length === 1 || yCoordinates.length === 1) {
|
|
286436
286615
|
topology = "linear";
|
|
286437
286616
|
} else if (xCoordinates.length <= 2 || yCoordinates.length <= 2) {
|
|
286438
286617
|
topology = "two-sided";
|
|
286439
|
-
} else if (
|
|
286618
|
+
} else if (topologyPads.length >= 4 && xCoordinates.length >= 2 && yCoordinates.length >= 2 && gridOccupancy >= 0.68) {
|
|
286440
286619
|
topology = "grid";
|
|
286441
|
-
} else if (hasPadsOnFourSides &&
|
|
286620
|
+
} else if (hasPadsOnFourSides && topologyPads.length >= 8) {
|
|
286442
286621
|
topology = "four-sided";
|
|
286443
286622
|
}
|
|
286444
286623
|
const pitch = getPitchEstimate(target.pads, tolerance);
|
|
286445
286624
|
const medianPadLongSide = median(padBounds.map((bound) => Math.max(bound.width, bound.height)));
|
|
286446
286625
|
const medianPadShortSide = median(padBounds.map((bound) => Math.min(bound.width, bound.height)));
|
|
286447
286626
|
const medianPadDiameter = median(padBounds.map((bound) => Math.sqrt(bound.width * bound.height)));
|
|
286627
|
+
const medianHoleDiameter = median(target.pads.flatMap((pad2) => pad2.hole ? [Math.sqrt(pad2.hole.width * pad2.hole.height)] : []));
|
|
286448
286628
|
const platedHoleCount = target.pads.filter((pad2) => pad2.kind === "plated-hole").length;
|
|
286449
286629
|
const insetQuadAdjustment = topology === "four-sided" ? 0.2 : 0;
|
|
286450
286630
|
return {
|
|
@@ -286454,7 +286634,7 @@ var analyzeTarget = (target) => {
|
|
|
286454
286634
|
heuristics: {
|
|
286455
286635
|
ball: medianPadDiameter,
|
|
286456
286636
|
h: bounds.height + insetQuadAdjustment,
|
|
286457
|
-
id: medianPadDiameter * 0.6,
|
|
286637
|
+
id: medianHoleDiameter || medianPadDiameter * 0.6,
|
|
286458
286638
|
od: medianPadDiameter,
|
|
286459
286639
|
p: pitch,
|
|
286460
286640
|
pad: medianPadDiameter,
|
|
@@ -286463,10 +286643,17 @@ var analyzeTarget = (target) => {
|
|
|
286463
286643
|
pw: medianPadShortSide,
|
|
286464
286644
|
w: bounds.width + insetQuadAdjustment
|
|
286465
286645
|
},
|
|
286646
|
+
horizontalSidePadCount: Math.max(leftSidePadCount, rightSidePadCount),
|
|
286647
|
+
lgaPadLength: leftRightEdgePads.length ? median(leftRightEdgePads.map(({ bounds: bound }) => bound.width)) : median(topBottomEdgePads.map(({ bounds: bound }) => bound.height)),
|
|
286648
|
+
lgaPadWidth: leftRightEdgePads.length ? median(leftRightEdgePads.map(({ bounds: bound }) => bound.height)) : median(topBottomEdgePads.map(({ bounds: bound }) => bound.width)),
|
|
286466
286649
|
perimeterPadCount: sidePads.length,
|
|
286467
286650
|
platedHoleCount,
|
|
286468
|
-
thermalPad:
|
|
286469
|
-
|
|
286651
|
+
thermalPad: thermalPadEntry ? {
|
|
286652
|
+
height: thermalPadEntry.bound.height,
|
|
286653
|
+
width: thermalPadEntry.bound.width
|
|
286654
|
+
} : undefined,
|
|
286655
|
+
topology,
|
|
286656
|
+
verticalSidePadCount: Math.max(bottomSidePadCount, topSidePadCount)
|
|
286470
286657
|
};
|
|
286471
286658
|
};
|
|
286472
286659
|
var normalizePads = (pads) => {
|
|
@@ -286479,8 +286666,28 @@ var normalizePads = (pads) => {
|
|
|
286479
286666
|
y: pad2.y - centerY
|
|
286480
286667
|
}));
|
|
286481
286668
|
};
|
|
286669
|
+
var rotateFootprint = (footprint, rotation11) => {
|
|
286670
|
+
if (rotation11 === 0)
|
|
286671
|
+
return footprint;
|
|
286672
|
+
const radians = rotation11 * Math.PI / 180;
|
|
286673
|
+
return {
|
|
286674
|
+
...footprint,
|
|
286675
|
+
pads: footprint.pads.map((pad2) => ({
|
|
286676
|
+
...pad2,
|
|
286677
|
+
hole: pad2.hole ? {
|
|
286678
|
+
...pad2.hole,
|
|
286679
|
+
offsetX: pad2.hole.offsetX * Math.cos(radians) - pad2.hole.offsetY * Math.sin(radians),
|
|
286680
|
+
offsetY: pad2.hole.offsetX * Math.sin(radians) + pad2.hole.offsetY * Math.cos(radians),
|
|
286681
|
+
rotation: (pad2.hole.rotation + rotation11) % 360
|
|
286682
|
+
} : undefined,
|
|
286683
|
+
rotation: (pad2.rotation + rotation11) % 360,
|
|
286684
|
+
x: pad2.x * Math.cos(radians) - pad2.y * Math.sin(radians),
|
|
286685
|
+
y: pad2.x * Math.sin(radians) + pad2.y * Math.cos(radians)
|
|
286686
|
+
}))
|
|
286687
|
+
};
|
|
286688
|
+
};
|
|
286482
286689
|
var getOrientedPadSize = (pad2) => {
|
|
286483
|
-
const bounds =
|
|
286690
|
+
const bounds = getPadBounds3({ ...pad2, x: 0, y: 0 });
|
|
286484
286691
|
return { height: bounds.height, width: bounds.width };
|
|
286485
286692
|
};
|
|
286486
286693
|
var matchPadsByPosition = (left, right) => {
|
|
@@ -286521,6 +286728,22 @@ var getGeometryLoss = (candidate, target) => {
|
|
|
286521
286728
|
loss += 4;
|
|
286522
286729
|
if (candidatePad.shape !== targetPad.shape)
|
|
286523
286730
|
loss += 0.08;
|
|
286731
|
+
if (Boolean(candidatePad.hole) !== Boolean(targetPad.hole)) {
|
|
286732
|
+
loss += 4;
|
|
286733
|
+
} else if (candidatePad.hole && targetPad.hole) {
|
|
286734
|
+
const holeWidthScale = Math.max(targetPad.hole.width, 0.05);
|
|
286735
|
+
const holeHeightScale = Math.max(targetPad.hole.height, 0.05);
|
|
286736
|
+
const holeWidthDifference = (candidatePad.hole.width - targetPad.hole.width) / holeWidthScale;
|
|
286737
|
+
const holeHeightDifference = (candidatePad.hole.height - targetPad.hole.height) / holeHeightScale;
|
|
286738
|
+
const holeOffsetXDifference = (candidatePad.hole.offsetX - targetPad.hole.offsetX) / positionScale;
|
|
286739
|
+
const holeOffsetYDifference = (candidatePad.hole.offsetY - targetPad.hole.offsetY) / positionScale;
|
|
286740
|
+
loss += holeWidthDifference * holeWidthDifference + holeHeightDifference * holeHeightDifference + holeOffsetXDifference * holeOffsetXDifference * 4 + holeOffsetYDifference * holeOffsetYDifference * 4;
|
|
286741
|
+
if (candidatePad.hole.shape !== targetPad.hole.shape)
|
|
286742
|
+
loss += 0.08;
|
|
286743
|
+
}
|
|
286744
|
+
if (targetPad.portHints.length > 0 && !candidatePad.portHints.some((hint) => targetPad.portHints.includes(hint))) {
|
|
286745
|
+
loss += 0.04;
|
|
286746
|
+
}
|
|
286524
286747
|
}
|
|
286525
286748
|
return loss / pairs4.length;
|
|
286526
286749
|
};
|
|
@@ -286530,11 +286753,13 @@ var getDomainScore = (target, family) => {
|
|
|
286530
286753
|
const aliases2 = {
|
|
286531
286754
|
cap: ["capacitor", "cap"],
|
|
286532
286755
|
dfn: ["dfn"],
|
|
286756
|
+
lga: ["lga"],
|
|
286533
286757
|
qfn: ["qfn"],
|
|
286534
286758
|
res: ["resistor", "res"],
|
|
286535
286759
|
soic: ["soic", "so-"],
|
|
286536
286760
|
ssop: ["ssop"],
|
|
286537
|
-
tssop: ["tssop"]
|
|
286761
|
+
tssop: ["tssop"],
|
|
286762
|
+
usbcmidmount: ["usb-c", "usb c", "type-c", "type c", "usbc"]
|
|
286538
286763
|
};
|
|
286539
286764
|
const terms = aliases2[family] ?? [family];
|
|
286540
286765
|
return terms.some((term) => description.includes(term)) ? 1 : 0;
|
|
@@ -286559,7 +286784,71 @@ var buildParameterizedString = (seed, parameters) => {
|
|
|
286559
286784
|
}).join("_");
|
|
286560
286785
|
return suffix ? `${seed}_${suffix}` : seed;
|
|
286561
286786
|
};
|
|
286562
|
-
var geometrySignature = (preview) => preview.pads.map((pad2) =>
|
|
286787
|
+
var geometrySignature = (preview) => preview.pads.map((pad2) => {
|
|
286788
|
+
const holeSignature = pad2.hole ? [
|
|
286789
|
+
pad2.hole.shape,
|
|
286790
|
+
pad2.hole.offsetX,
|
|
286791
|
+
pad2.hole.offsetY,
|
|
286792
|
+
pad2.hole.width,
|
|
286793
|
+
pad2.hole.height,
|
|
286794
|
+
pad2.hole.rotation
|
|
286795
|
+
].join(":") : "no-hole";
|
|
286796
|
+
const pointSignature = pad2.shape === "polygon" ? pad2.points?.map((point9) => `${point9.x}:${point9.y}`).join(",") : "no-points";
|
|
286797
|
+
return [
|
|
286798
|
+
pad2.kind,
|
|
286799
|
+
pad2.shape,
|
|
286800
|
+
pad2.x,
|
|
286801
|
+
pad2.y,
|
|
286802
|
+
pad2.width,
|
|
286803
|
+
pad2.height,
|
|
286804
|
+
pad2.rotation,
|
|
286805
|
+
pointSignature,
|
|
286806
|
+
holeSignature
|
|
286807
|
+
].map(String).join(":");
|
|
286808
|
+
}).join("|");
|
|
286809
|
+
var padShapeSignature = (preview) => preview.pads.map((pad2) => `${pad2.kind}:${pad2.shape}`).toSorted().join("|");
|
|
286810
|
+
var areClose = (left, right) => Math.abs(left - right) <= 0.00001;
|
|
286811
|
+
var areSamePoint = (left, right) => areClose(left.x, right.x) && areClose(left.y, right.y);
|
|
286812
|
+
var haveSamePolygon = (left, right) => {
|
|
286813
|
+
if (left.shape !== "polygon" && right.shape !== "polygon")
|
|
286814
|
+
return true;
|
|
286815
|
+
if (left.shape !== "polygon" || right.shape !== "polygon")
|
|
286816
|
+
return false;
|
|
286817
|
+
const leftPoints = getPolygonWorldPoints(left);
|
|
286818
|
+
const rightPoints = getPolygonWorldPoints(right);
|
|
286819
|
+
if (leftPoints.length !== rightPoints.length)
|
|
286820
|
+
return false;
|
|
286821
|
+
const matchesFrom = (startIndex, direction2) => leftPoints.every((leftPoint, index) => {
|
|
286822
|
+
const rightIndex = (startIndex + direction2 * index + rightPoints.length) % rightPoints.length;
|
|
286823
|
+
return areSamePoint(leftPoint, rightPoints[rightIndex]);
|
|
286824
|
+
});
|
|
286825
|
+
return rightPoints.some((rightPoint, startIndex) => areSamePoint(leftPoints[0], rightPoint) && (matchesFrom(startIndex, 1) || matchesFrom(startIndex, -1)));
|
|
286826
|
+
};
|
|
286827
|
+
var haveSameOrientedPads = (left, right) => {
|
|
286828
|
+
if (left.pads.length !== right.pads.length)
|
|
286829
|
+
return false;
|
|
286830
|
+
return left.pads.every((leftPad, index) => {
|
|
286831
|
+
const rightPad = right.pads[index];
|
|
286832
|
+
if (!rightPad)
|
|
286833
|
+
return false;
|
|
286834
|
+
const leftSize = getOrientedPadSize(leftPad);
|
|
286835
|
+
const rightSize = getOrientedPadSize(rightPad);
|
|
286836
|
+
const holesMatch = !leftPad.hole && !rightPad.hole ? true : Boolean(leftPad.hole && rightPad.hole) && leftPad.hole?.shape === rightPad.hole?.shape && areClose(leftPad.hole?.offsetX ?? 0, rightPad.hole?.offsetX ?? 0) && areClose(leftPad.hole?.offsetY ?? 0, rightPad.hole?.offsetY ?? 0) && areClose(leftPad.hole?.width ?? 0, rightPad.hole?.width ?? 0) && areClose(leftPad.hole?.height ?? 0, rightPad.hole?.height ?? 0) && areClose(leftPad.hole?.rotation ?? 0, rightPad.hole?.rotation ?? 0);
|
|
286837
|
+
return leftPad.kind === rightPad.kind && leftPad.shape === rightPad.shape && leftPad.portHints.join("|") === rightPad.portHints.join("|") && areClose(leftPad.x, rightPad.x) && areClose(leftPad.y, rightPad.y) && areClose(leftSize.width, rightSize.width) && areClose(leftSize.height, rightSize.height) && haveSamePolygon(leftPad, rightPad) && holesMatch;
|
|
286838
|
+
});
|
|
286839
|
+
};
|
|
286840
|
+
var encodeOrientationInFootprinterString = (footprinterString, searchRotation, orientedPreview) => {
|
|
286841
|
+
if (searchRotation === 0)
|
|
286842
|
+
return footprinterString;
|
|
286843
|
+
for (const [side, alignment] of PIN1_LOCATIONS) {
|
|
286844
|
+
const orientedString = `${footprinterString}_pin1location(${side},${alignment})`;
|
|
286845
|
+
const preview = tryBuild(orientedString);
|
|
286846
|
+
if (preview && haveSameOrientedPads(preview, orientedPreview)) {
|
|
286847
|
+
return orientedString;
|
|
286848
|
+
}
|
|
286849
|
+
}
|
|
286850
|
+
return null;
|
|
286851
|
+
};
|
|
286563
286852
|
var getPreferredFamilies = (analysis) => {
|
|
286564
286853
|
if (analysis.platedHoleCount > analysis.perimeterPadCount / 2) {
|
|
286565
286854
|
return /* @__PURE__ */ new Set([
|
|
@@ -286569,16 +286858,18 @@ var getPreferredFamilies = (analysis) => {
|
|
|
286569
286858
|
"pinrow",
|
|
286570
286859
|
"radial",
|
|
286571
286860
|
"to220",
|
|
286572
|
-
"to92"
|
|
286861
|
+
"to92",
|
|
286862
|
+
"usbcmidmount"
|
|
286573
286863
|
]);
|
|
286574
286864
|
}
|
|
286575
286865
|
if (analysis.topology === "grid")
|
|
286576
286866
|
return /* @__PURE__ */ new Set(["bga"]);
|
|
286577
286867
|
if (analysis.topology === "four-sided") {
|
|
286578
|
-
return /* @__PURE__ */ new Set(["lqfp", "mlp", "qfn", "qfp", "quad", "tqfp"]);
|
|
286868
|
+
return /* @__PURE__ */ new Set(["lga", "lqfp", "mlp", "qfn", "qfp", "quad", "tqfp"]);
|
|
286579
286869
|
}
|
|
286580
286870
|
if (analysis.topology === "two-sided") {
|
|
286581
286871
|
return /* @__PURE__ */ new Set([
|
|
286872
|
+
"lga",
|
|
286582
286873
|
"soic",
|
|
286583
286874
|
"tssop",
|
|
286584
286875
|
"ssop",
|
|
@@ -286599,20 +286890,43 @@ var generateSeeds = (target, analysis) => {
|
|
|
286599
286890
|
const seeds = /* @__PURE__ */ new Set;
|
|
286600
286891
|
for (const family of getFootprintNames2()) {
|
|
286601
286892
|
seeds.add(`${family}${padCount}`);
|
|
286602
|
-
|
|
286893
|
+
if (family !== "usbcmidmount")
|
|
286894
|
+
seeds.add(family);
|
|
286603
286895
|
}
|
|
286604
286896
|
if (analysis.thermalPad && analysis.perimeterPadCount > 0) {
|
|
286605
|
-
const
|
|
286606
|
-
|
|
286897
|
+
const thermalPadDimensionOptions = /* @__PURE__ */ new Set([
|
|
286898
|
+
`${formatMillimeters(analysis.thermalPad.width)}x${formatMillimeters(analysis.thermalPad.height)}`,
|
|
286899
|
+
`${formatMillimeters(analysis.thermalPad.height)}x${formatMillimeters(analysis.thermalPad.width)}`
|
|
286900
|
+
]);
|
|
286901
|
+
const thermalPadFamilies = analysis.topology === "two-sided" ? ["dfn", "msop", "soic", "ssop", "tssop", "vssop"] : ["mlp", "qfn", "quad"];
|
|
286902
|
+
for (const family of thermalPadFamilies) {
|
|
286607
286903
|
seeds.add(`${family}${analysis.perimeterPadCount}_thermalpad`);
|
|
286608
|
-
|
|
286904
|
+
for (const thermalPadDimensions of thermalPadDimensionOptions) {
|
|
286905
|
+
seeds.add(`${family}${analysis.perimeterPadCount}_thermalpad${thermalPadDimensions}`);
|
|
286906
|
+
}
|
|
286609
286907
|
}
|
|
286610
286908
|
}
|
|
286611
286909
|
if (analysis.topology === "grid") {
|
|
286612
286910
|
seeds.add(`bga${padCount}_grid${analysis.gridColumns}x${analysis.gridRows}`);
|
|
286613
286911
|
}
|
|
286912
|
+
if (analysis.topology === "four-sided" && 2 * (analysis.horizontalSidePadCount + analysis.verticalSidePadCount) === analysis.perimeterPadCount) {
|
|
286913
|
+
seeds.add(`lga${analysis.perimeterPadCount}_grid${analysis.horizontalSidePadCount}x${analysis.verticalSidePadCount}`);
|
|
286914
|
+
}
|
|
286915
|
+
const hasLgaHint = target.sourceHints?.some((hint) => hint.toLowerCase().includes("lga"));
|
|
286916
|
+
if (analysis.topology === "two-sided" && hasLgaHint) {
|
|
286917
|
+
const padsPerSide = analysis.perimeterPadCount / 2;
|
|
286918
|
+
if (Number.isInteger(padsPerSide)) {
|
|
286919
|
+
const grid = analysis.gridColumns <= 2 ? `${padsPerSide}x0` : `0x${padsPerSide}`;
|
|
286920
|
+
seeds.add(`lga${analysis.perimeterPadCount}_grid${grid}`);
|
|
286921
|
+
}
|
|
286922
|
+
}
|
|
286923
|
+
if (analysis.topology === "two-sided" && padCount % 2 === 1) {
|
|
286924
|
+
for (let missingPosition = 1;missingPosition <= padCount + 1; missingPosition += 1) {
|
|
286925
|
+
seeds.add(`dfn${padCount + 1}_missing(${missingPosition})`);
|
|
286926
|
+
}
|
|
286927
|
+
}
|
|
286614
286928
|
if (padCount === 2 && analysis.platedHoleCount === 0) {
|
|
286615
|
-
const padBounds = target.pads.map(
|
|
286929
|
+
const padBounds = target.pads.map(getPadBounds3);
|
|
286616
286930
|
const passiveDimensions = `p${formatMillimeters(analysis.heuristics.p)}_pw${formatMillimeters(median(padBounds.map((bound) => bound.width)))}_ph${formatMillimeters(median(padBounds.map((bound) => bound.height)))}`;
|
|
286617
286931
|
seeds.add(`res_${passiveDimensions}`);
|
|
286618
286932
|
seeds.add(`cap_${passiveDimensions}`);
|
|
@@ -286622,18 +286936,49 @@ var generateSeeds = (target, analysis) => {
|
|
|
286622
286936
|
seeds.add(`res${size.imperial}`);
|
|
286623
286937
|
}
|
|
286624
286938
|
}
|
|
286939
|
+
if (target.pads.some((pad2) => pad2.shape === "pill")) {
|
|
286940
|
+
for (const seed of [...seeds]) {
|
|
286941
|
+
const pillPadSeed = `${seed}_pillpads`;
|
|
286942
|
+
const preview = tryBuild(pillPadSeed);
|
|
286943
|
+
if (preview?.pads.length === padCount && preview.pads.some((pad2) => pad2.shape === "pill")) {
|
|
286944
|
+
seeds.add(pillPadSeed);
|
|
286945
|
+
}
|
|
286946
|
+
}
|
|
286947
|
+
}
|
|
286625
286948
|
return [...seeds];
|
|
286626
286949
|
};
|
|
286627
|
-
var selectSeedsToOptimize = (candidates, analysis) => {
|
|
286950
|
+
var selectSeedsToOptimize = (candidates, analysis, target) => {
|
|
286628
286951
|
const selected = /* @__PURE__ */ new Map;
|
|
286952
|
+
const targetPadShapeSignature = padShapeSignature(target);
|
|
286953
|
+
const selectedShapeFamilies = /* @__PURE__ */ new Set;
|
|
286954
|
+
if (analysis.topology === "four-sided" || analysis.topology === "two-sided") {
|
|
286955
|
+
const lgaGridCandidate = candidates.find((candidate) => candidate.family === "lga" && candidate.footprinterString.includes("_grid") && candidate.searchRotation === 0);
|
|
286956
|
+
if (lgaGridCandidate) {
|
|
286957
|
+
selected.set(`${lgaGridCandidate.footprinterString}:${lgaGridCandidate.searchRotation}`, lgaGridCandidate);
|
|
286958
|
+
}
|
|
286959
|
+
}
|
|
286960
|
+
if (target.pads.some((pad2) => pad2.shape === "pill")) {
|
|
286961
|
+
for (const candidate of candidates) {
|
|
286962
|
+
if (padShapeSignature(candidate.preview) !== targetPadShapeSignature) {
|
|
286963
|
+
continue;
|
|
286964
|
+
}
|
|
286965
|
+
if (selectedShapeFamilies.has(candidate.family))
|
|
286966
|
+
continue;
|
|
286967
|
+
selectedShapeFamilies.add(candidate.family);
|
|
286968
|
+
selected.set(`${candidate.footprinterString}:${candidate.searchRotation}`, candidate);
|
|
286969
|
+
if (selected.size >= 4)
|
|
286970
|
+
break;
|
|
286971
|
+
}
|
|
286972
|
+
}
|
|
286629
286973
|
for (const candidate of candidates.slice(0, 4)) {
|
|
286630
|
-
selected.set(candidate.footprinterString
|
|
286974
|
+
selected.set(`${candidate.footprinterString}:${candidate.searchRotation}`, candidate);
|
|
286631
286975
|
}
|
|
286632
286976
|
const preferredFamilies = getPreferredFamilies(analysis);
|
|
286633
286977
|
for (const family of preferredFamilies) {
|
|
286634
|
-
const candidate = candidates.find((entry) => entry.family === family && entry.footprinterString === family) ?? candidates.find((entry) => entry.family === family);
|
|
286635
|
-
if (candidate)
|
|
286636
|
-
selected.set(candidate.footprinterString
|
|
286978
|
+
const candidate = (analysis.thermalPad ? candidates.find((entry) => entry.family === family && entry.footprinterString.includes("_thermalpad")) : undefined) ?? (family === "dfn" ? candidates.find((entry) => entry.family === family && entry.footprinterString.includes("_missing(")) : undefined) ?? (family === "lga" ? candidates.find((entry) => entry.family === family && entry.footprinterString.includes("_grid")) : undefined) ?? candidates.find((entry) => entry.family === family && entry.footprinterString === family) ?? candidates.find((entry) => entry.family === family);
|
|
286979
|
+
if (candidate) {
|
|
286980
|
+
selected.set(`${candidate.footprinterString}:${candidate.searchRotation}`, candidate);
|
|
286981
|
+
}
|
|
286637
286982
|
if (selected.size >= MAX_OPTIMIZED_SEEDS)
|
|
286638
286983
|
break;
|
|
286639
286984
|
}
|
|
@@ -286642,12 +286987,14 @@ var selectSeedsToOptimize = (candidates, analysis) => {
|
|
|
286642
286987
|
var findActiveParameters = (seed, analysis) => {
|
|
286643
286988
|
const active = [];
|
|
286644
286989
|
const baseSignature = geometrySignature(seed.preview);
|
|
286990
|
+
const heuristics = getOrientedHeuristics(seed, analysis);
|
|
286645
286991
|
for (const parameter of NUMERIC_PARAMETERS) {
|
|
286646
|
-
const heuristic = Math.max(
|
|
286992
|
+
const heuristic = Math.max(heuristics[parameter], 0.05);
|
|
286647
286993
|
const preview = tryBuild(buildParameterizedString(seed.footprinterString, {
|
|
286648
286994
|
[parameter]: heuristic
|
|
286649
286995
|
}));
|
|
286650
|
-
|
|
286996
|
+
const orientedPreview = preview ? rotateFootprint(preview, seed.searchRotation) : null;
|
|
286997
|
+
if (orientedPreview && orientedPreview.pads.length === seed.preview.pads.length && geometrySignature(orientedPreview) !== baseSignature) {
|
|
286651
286998
|
active.push(parameter);
|
|
286652
286999
|
}
|
|
286653
287000
|
}
|
|
@@ -286662,13 +287009,17 @@ var optimizeSeed = (seed, target, analysis) => {
|
|
|
286662
287009
|
};
|
|
286663
287010
|
}
|
|
286664
287011
|
const values = {};
|
|
287012
|
+
const heuristics = getOrientedHeuristics(seed, analysis);
|
|
286665
287013
|
for (const parameter of activeParameters) {
|
|
286666
|
-
values[parameter] = Math.max(
|
|
287014
|
+
values[parameter] = Math.max(heuristics[parameter], 0.05);
|
|
286667
287015
|
}
|
|
286668
287016
|
const evaluate = (parameters) => {
|
|
286669
287017
|
const footprinterString = buildParameterizedString(seed.footprinterString, parameters);
|
|
286670
|
-
const
|
|
286671
|
-
if (!
|
|
287018
|
+
const unrotatedPreview = tryBuild(footprinterString);
|
|
287019
|
+
if (!unrotatedPreview)
|
|
287020
|
+
return null;
|
|
287021
|
+
const preview = rotateFootprint(unrotatedPreview, seed.searchRotation);
|
|
287022
|
+
if (preview.pads.length !== target.pads.length) {
|
|
286672
287023
|
return null;
|
|
286673
287024
|
}
|
|
286674
287025
|
return {
|
|
@@ -286710,9 +287061,9 @@ var optimizeSeed = (seed, target, analysis) => {
|
|
|
286710
287061
|
secondMoments[parameter] = secondMoment;
|
|
286711
287062
|
const correctedFirstMoment = firstMoment / (1 - 0.9 ** iteration);
|
|
286712
287063
|
const correctedSecondMoment = secondMoment / (1 - 0.999 ** iteration);
|
|
286713
|
-
const stepScale = Math.max(
|
|
287064
|
+
const stepScale = Math.max(heuristics[parameter] * 0.075, 0.025);
|
|
286714
287065
|
const currentValue = values[parameter] ?? 0.05;
|
|
286715
|
-
const upperBound2 = Math.max(analysis.bounds.width, analysis.bounds.height,
|
|
287066
|
+
const upperBound2 = Math.max(analysis.bounds.width, analysis.bounds.height, heuristics[parameter]) * 3;
|
|
286716
287067
|
values[parameter] = clamp5(currentValue - stepScale * correctedFirstMoment / (Math.sqrt(correctedSecondMoment) + 0.00000001), 0.025, Math.max(upperBound2, 0.1));
|
|
286717
287068
|
}
|
|
286718
287069
|
current2 = evaluate(values);
|
|
@@ -286734,17 +287085,20 @@ var optimizeSeed = (seed, target, analysis) => {
|
|
|
286734
287085
|
const withoutParameter = { ...simplifiedParameters };
|
|
286735
287086
|
delete withoutParameter[parameter];
|
|
286736
287087
|
const simplifiedPreview2 = tryBuild(buildParameterizedString(seed.footprinterString, withoutParameter));
|
|
286737
|
-
|
|
287088
|
+
const orientedSimplifiedPreview = simplifiedPreview2 ? rotateFootprint(simplifiedPreview2, seed.searchRotation) : null;
|
|
287089
|
+
if (orientedSimplifiedPreview && geometrySignature(orientedSimplifiedPreview) === bestSignature) {
|
|
286738
287090
|
delete simplifiedParameters[parameter];
|
|
286739
287091
|
}
|
|
286740
287092
|
}
|
|
286741
287093
|
const simplifiedString = buildParameterizedString(seed.footprinterString, simplifiedParameters);
|
|
286742
|
-
const
|
|
287094
|
+
const unrotatedSimplifiedPreview = tryBuild(simplifiedString);
|
|
287095
|
+
const simplifiedPreview = unrotatedSimplifiedPreview ? rotateFootprint(unrotatedSimplifiedPreview, seed.searchRotation) : best.preview;
|
|
286743
287096
|
return {
|
|
286744
287097
|
family: seed.family,
|
|
286745
287098
|
footprinterString: simplifiedString,
|
|
286746
287099
|
geometryScore: 1 / (1 + best.loss),
|
|
286747
287100
|
optimizedParameters: simplifiedParameters,
|
|
287101
|
+
searchRotation: seed.searchRotation,
|
|
286748
287102
|
preview: simplifiedPreview
|
|
286749
287103
|
};
|
|
286750
287104
|
};
|
|
@@ -286752,44 +287106,63 @@ var discoverFootprinterString = (target, maxCandidates = 5) => {
|
|
|
286752
287106
|
const analysis = analyzeTarget(target);
|
|
286753
287107
|
const rawSeeds = generateSeeds(target, analysis);
|
|
286754
287108
|
const seedCandidates = rawSeeds.flatMap((footprinterString) => {
|
|
286755
|
-
const
|
|
286756
|
-
if (!
|
|
287109
|
+
const unrotatedPreview = tryBuild(footprinterString);
|
|
287110
|
+
if (!unrotatedPreview || unrotatedPreview.pads.length !== target.pads.length) {
|
|
286757
287111
|
return [];
|
|
286758
|
-
|
|
286759
|
-
|
|
286760
|
-
|
|
286761
|
-
|
|
286762
|
-
|
|
286763
|
-
|
|
286764
|
-
|
|
286765
|
-
|
|
286766
|
-
|
|
286767
|
-
|
|
286768
|
-
|
|
287112
|
+
}
|
|
287113
|
+
return FOOTPRINT_ROTATIONS.flatMap((searchRotation) => {
|
|
287114
|
+
const preview = rotateFootprint(unrotatedPreview, searchRotation);
|
|
287115
|
+
const platedHoleCount = preview.pads.filter((pad2) => pad2.kind === "plated-hole").length;
|
|
287116
|
+
if (platedHoleCount !== analysis.platedHoleCount)
|
|
287117
|
+
return [];
|
|
287118
|
+
return [
|
|
287119
|
+
{
|
|
287120
|
+
family: getFamily(footprinterString),
|
|
287121
|
+
footprinterString,
|
|
287122
|
+
geometryScore: getGeometryScore(preview, target),
|
|
287123
|
+
preview,
|
|
287124
|
+
searchRotation
|
|
287125
|
+
}
|
|
287126
|
+
];
|
|
287127
|
+
});
|
|
286769
287128
|
});
|
|
286770
|
-
seedCandidates.sort((left, right) => right.geometryScore - left.geometryScore);
|
|
286771
|
-
const selectedSeeds = selectSeedsToOptimize(seedCandidates, analysis);
|
|
287129
|
+
seedCandidates.sort((left, right) => right.geometryScore - left.geometryScore || left.searchRotation - right.searchRotation);
|
|
287130
|
+
const selectedSeeds = selectSeedsToOptimize(seedCandidates, analysis, target);
|
|
286772
287131
|
const optimized = selectedSeeds.map((seed) => optimizeSeed(seed, target, analysis));
|
|
287132
|
+
const targetPadShapeSignature = padShapeSignature(target);
|
|
286773
287133
|
const allCandidates = [...optimized, ...seedCandidates].map((candidate) => {
|
|
286774
|
-
const copperIntersectionOverUnion = summarizeCopperComparison(candidate.preview, target, SEARCH_GRID_SIZE)
|
|
287134
|
+
const { copperIntersectionOverUnion, holeIntersectionOverUnion } = summarizeCopperComparison(candidate.preview, target, SEARCH_GRID_SIZE);
|
|
286775
287135
|
const domainScore = getDomainScore(target, candidate.family);
|
|
287136
|
+
const shapesMatch = padShapeSignature(candidate.preview) === targetPadShapeSignature;
|
|
286776
287137
|
return {
|
|
286777
287138
|
copperIntersectionOverUnion,
|
|
286778
287139
|
domainScore,
|
|
286779
287140
|
family: candidate.family,
|
|
286780
287141
|
footprinterString: candidate.footprinterString,
|
|
286781
287142
|
geometryScore: candidate.geometryScore,
|
|
287143
|
+
holeIntersectionOverUnion,
|
|
286782
287144
|
optimizedParameters: "optimizedParameters" in candidate ? candidate.optimizedParameters : {},
|
|
286783
|
-
|
|
287145
|
+
preview: candidate.preview,
|
|
287146
|
+
rankingScore: copperIntersectionOverUnion + (holeIntersectionOverUnion - 1) * 0.12 + domainScore * (shapesMatch ? 0.08 : 0.01),
|
|
287147
|
+
searchRotation: candidate.searchRotation
|
|
286784
287148
|
};
|
|
286785
|
-
}).sort((left, right) => right.rankingScore - left.rankingScore || right.copperIntersectionOverUnion - left.copperIntersectionOverUnion || right.domainScore - left.domainScore || right.geometryScore - left.geometryScore || left.footprinterString.length - right.footprinterString.length);
|
|
287149
|
+
}).sort((left, right) => right.rankingScore - left.rankingScore || right.copperIntersectionOverUnion - left.copperIntersectionOverUnion || right.holeIntersectionOverUnion - left.holeIntersectionOverUnion || right.domainScore - left.domainScore || right.geometryScore - left.geometryScore || left.searchRotation - right.searchRotation || left.footprinterString.length - right.footprinterString.length);
|
|
286786
287150
|
const uniqueCandidates = [];
|
|
286787
287151
|
const seenStrings = /* @__PURE__ */ new Set;
|
|
286788
287152
|
for (const candidate of allCandidates) {
|
|
286789
|
-
|
|
287153
|
+
const orientedString = encodeOrientationInFootprinterString(candidate.footprinterString, candidate.searchRotation, candidate.preview);
|
|
287154
|
+
if (!orientedString || seenStrings.has(orientedString))
|
|
286790
287155
|
continue;
|
|
286791
|
-
seenStrings.add(
|
|
286792
|
-
|
|
287156
|
+
seenStrings.add(orientedString);
|
|
287157
|
+
const {
|
|
287158
|
+
preview: _preview,
|
|
287159
|
+
searchRotation: _searchRotation,
|
|
287160
|
+
...publicData
|
|
287161
|
+
} = candidate;
|
|
287162
|
+
uniqueCandidates.push({
|
|
287163
|
+
...publicData,
|
|
287164
|
+
footprinterString: orientedString
|
|
287165
|
+
});
|
|
286793
287166
|
if (uniqueCandidates.length >= clamp5(maxCandidates, 1, 10))
|
|
286794
287167
|
break;
|
|
286795
287168
|
}
|
|
@@ -286810,6 +287183,9 @@ var circuitJsonToFootprinter = (circuitJson, options = {}) => {
|
|
|
286810
287183
|
return discoverFootprinterString(target, options.maxCandidates);
|
|
286811
287184
|
};
|
|
286812
287185
|
|
|
287186
|
+
// lib/shared/circuit-json-to-footprinter.ts
|
|
287187
|
+
var circuitJsonToFootprinter2 = circuitJsonToFootprinter;
|
|
287188
|
+
|
|
286813
287189
|
// cli/convert/discover-footprinter-from-file.ts
|
|
286814
287190
|
import fs51 from "node:fs/promises";
|
|
286815
287191
|
import path56 from "node:path";
|
|
@@ -286853,7 +287229,7 @@ var discoverFootprinterFromFile = async ({
|
|
|
286853
287229
|
}) => {
|
|
286854
287230
|
const circuitJson = await loadCircuitJsonForFootprinter(inputPath);
|
|
286855
287231
|
const inputText = await fs51.readFile(inputPath, "utf-8");
|
|
286856
|
-
const result =
|
|
287232
|
+
const result = circuitJsonToFootprinter2(circuitJson, {
|
|
286857
287233
|
sourceHints: [path56.basename(inputPath), inputText.slice(0, 20000)]
|
|
286858
287234
|
});
|
|
286859
287235
|
if (!result.best) {
|
|
@@ -291789,7 +292165,7 @@ import {
|
|
|
291789
292165
|
|
|
291790
292166
|
// node_modules/dsn-converter/dist/index.js
|
|
291791
292167
|
import { su as su14 } from "@tscircuit/soup-util";
|
|
291792
|
-
import { getBoundsFromPoints as
|
|
292168
|
+
import { getBoundsFromPoints as getBoundsFromPoints6 } from "@tscircuit/math-utils";
|
|
291793
292169
|
import { applyToPoint as applyToPoint31, scale as scale8 } from "transformation-matrix";
|
|
291794
292170
|
import { su as su24 } from "@tscircuit/soup-util";
|
|
291795
292171
|
import { su as su34 } from "@tscircuit/soup-util";
|
|
@@ -292033,9 +292409,9 @@ function getPolygonCentroid2(points) {
|
|
|
292033
292409
|
y: centroidY / areaFactor
|
|
292034
292410
|
};
|
|
292035
292411
|
}
|
|
292036
|
-
function
|
|
292412
|
+
function getPolygonSmtPadGeometry2(pad2) {
|
|
292037
292413
|
const points = getNormalizedPoints(pad2.points);
|
|
292038
|
-
const bounds =
|
|
292414
|
+
const bounds = getBoundsFromPoints6(points);
|
|
292039
292415
|
if (!bounds) {
|
|
292040
292416
|
return {
|
|
292041
292417
|
center: { x: 0, y: 0 },
|
|
@@ -292071,7 +292447,7 @@ function createAndAddPadstackFromPcbSmtPad({
|
|
|
292071
292447
|
}) {
|
|
292072
292448
|
const isCircle = pad2.shape === "circle";
|
|
292073
292449
|
const isPolygon = pad2.shape === "polygon";
|
|
292074
|
-
const polygonPadGeometry = isPolygon ?
|
|
292450
|
+
const polygonPadGeometry = isPolygon ? getPolygonSmtPadGeometry2(pad2) : undefined;
|
|
292075
292451
|
let shape = "rect";
|
|
292076
292452
|
let outerDiameter;
|
|
292077
292453
|
let holeDiameter;
|
|
@@ -292128,7 +292504,7 @@ function createPinForImage({
|
|
|
292128
292504
|
return;
|
|
292129
292505
|
const isCircle = pad2.shape === "circle";
|
|
292130
292506
|
const isPolygon = pad2.shape === "polygon";
|
|
292131
|
-
const polygonPadGeometry = isPolygon ?
|
|
292507
|
+
const polygonPadGeometry = isPolygon ? getPolygonSmtPadGeometry2(pad2) : undefined;
|
|
292132
292508
|
let shape = "rect";
|
|
292133
292509
|
let outerDiameter;
|
|
292134
292510
|
let holeDiameter;
|
|
@@ -304038,11 +304414,11 @@ function generateArcFromSweep(startX, startY, endX, endY, radius, largeArcFlag,
|
|
|
304038
304414
|
return path71;
|
|
304039
304415
|
}
|
|
304040
304416
|
var mil10ToMm = (value) => value * 10 * 0.0254;
|
|
304041
|
-
var
|
|
304417
|
+
var getBoundsCenter3 = (bounds) => ({
|
|
304042
304418
|
x: (bounds.minX + bounds.maxX) / 2,
|
|
304043
304419
|
y: (bounds.minY + bounds.maxY) / 2
|
|
304044
304420
|
});
|
|
304045
|
-
var
|
|
304421
|
+
var getBoundsFromPoints7 = (points) => {
|
|
304046
304422
|
if (points.length === 0) {
|
|
304047
304423
|
return null;
|
|
304048
304424
|
}
|
|
@@ -304108,8 +304484,8 @@ var getEasyEdaModelOriginOnBoardMm = ({
|
|
|
304108
304484
|
}
|
|
304109
304485
|
let modelOriginMm = null;
|
|
304110
304486
|
if (points.length > 0) {
|
|
304111
|
-
const bounds =
|
|
304112
|
-
modelOriginMm = bounds ?
|
|
304487
|
+
const bounds = getBoundsFromPoints7(points);
|
|
304488
|
+
modelOriginMm = bounds ? getBoundsCenter3(bounds) : null;
|
|
304113
304489
|
} else {
|
|
304114
304490
|
const [originX, originY] = String(svgNode.svgData.attrs?.c_origin ?? "0,0").split(",").map((value) => Number(value.trim()));
|
|
304115
304491
|
if (Number.isFinite(originX) && Number.isFinite(originY)) {
|
|
@@ -304142,7 +304518,7 @@ var getCadModelOffsetMmFromBounds = (easyEdaJson, bounds, {
|
|
|
304142
304518
|
const rotationDeg = (rotationZRaw % 360 + 360) % 360;
|
|
304143
304519
|
if (![0, 90, 180, 270].includes(rotationDeg))
|
|
304144
304520
|
return null;
|
|
304145
|
-
const modelCenter =
|
|
304521
|
+
const modelCenter = getBoundsCenter3(getXyBoundsFromModelBounds(bounds));
|
|
304146
304522
|
const footprintCenter = easyEdaJson.packageDetail.dataStr.head;
|
|
304147
304523
|
const footprintCenterMm = footprintBoundsCenterMm ?? {
|
|
304148
304524
|
x: mil10ToMm(footprintCenter.x),
|
|
@@ -311791,11 +312167,11 @@ function generateArcFromSweep2(startX, startY, endX, endY, radius, largeArcFlag,
|
|
|
311791
312167
|
return path71;
|
|
311792
312168
|
}
|
|
311793
312169
|
var mil10ToMm2 = (value) => value * 10 * 0.0254;
|
|
311794
|
-
var
|
|
312170
|
+
var getBoundsCenter4 = (bounds) => ({
|
|
311795
312171
|
x: (bounds.minX + bounds.maxX) / 2,
|
|
311796
312172
|
y: (bounds.minY + bounds.maxY) / 2
|
|
311797
312173
|
});
|
|
311798
|
-
var
|
|
312174
|
+
var getBoundsFromPoints8 = (points) => {
|
|
311799
312175
|
if (points.length === 0) {
|
|
311800
312176
|
return null;
|
|
311801
312177
|
}
|
|
@@ -311861,8 +312237,8 @@ var getEasyEdaModelOriginOnBoardMm2 = ({
|
|
|
311861
312237
|
}
|
|
311862
312238
|
let modelOriginMm = null;
|
|
311863
312239
|
if (points.length > 0) {
|
|
311864
|
-
const bounds =
|
|
311865
|
-
modelOriginMm = bounds ?
|
|
312240
|
+
const bounds = getBoundsFromPoints8(points);
|
|
312241
|
+
modelOriginMm = bounds ? getBoundsCenter4(bounds) : null;
|
|
311866
312242
|
} else {
|
|
311867
312243
|
const [originX, originY] = String(svgNode.svgData.attrs?.c_origin ?? "0,0").split(",").map((value) => Number(value.trim()));
|
|
311868
312244
|
if (Number.isFinite(originX) && Number.isFinite(originY)) {
|
|
@@ -311891,7 +312267,7 @@ var getCadModelOffsetMmFromBounds2 = (easyEdaJson, bounds, {
|
|
|
311891
312267
|
const rotationDeg = (rotationZRaw % 360 + 360) % 360;
|
|
311892
312268
|
if (![0, 90, 180, 270].includes(rotationDeg))
|
|
311893
312269
|
return null;
|
|
311894
|
-
const modelCenter =
|
|
312270
|
+
const modelCenter = getBoundsCenter4(getXyBoundsFromModelBounds2(bounds));
|
|
311895
312271
|
const footprintCenter = easyEdaJson.packageDetail.dataStr.head;
|
|
311896
312272
|
const footprintCenterMm = footprintBoundsCenterMm ?? {
|
|
311897
312273
|
x: mil10ToMm2(footprintCenter.x),
|
|
@@ -313452,7 +313828,7 @@ var convertImportedFootprintToFootprinter = ({
|
|
|
313452
313828
|
tsx
|
|
313453
313829
|
}) => {
|
|
313454
313830
|
try {
|
|
313455
|
-
const discovery =
|
|
313831
|
+
const discovery = circuitJsonToFootprinter2(circuitJson, {
|
|
313456
313832
|
maxCandidates: 5,
|
|
313457
313833
|
sourceHints
|
|
313458
313834
|
});
|