d8cb0dc06d
Web tool for designing nickel/copper busbars over cylindrical-cell battery packs (21700, 18650) in hex holders. Flask + build123d backend exports STEP/DXF/SVG; vanilla JS frontend with live preview, multi-project SQLite persistence, snapshot history. Deploy scripts in deploy/ (proxmox-lxc.sh, install.sh, update.sh).
112 lines
3.7 KiB
JavaScript
112 lines
3.7 KiB
JavaScript
/* importer.js — parse OpenSCAD ECHO / CSV / JSON / parametric generator.
|
|
*
|
|
* All parsers return an array of {id, x, y} in millimetres.
|
|
* Generator formulas are 1:1 mirrors of `get_hex_center_points_*` from
|
|
* Addy's hex_cell.scad (see CLAUDE.md for the cheat sheet).
|
|
*/
|
|
|
|
const Importer = (() => {
|
|
const COS30 = Math.cos(Math.PI / 6);
|
|
|
|
/** Parse OpenSCAD ECHO lines or loose `Cell N: x = …, y = …`. */
|
|
function parsePaste(text) {
|
|
const out = [];
|
|
const re = /Cell\s*(\d+)\s*:\s*x\s*=\s*(-?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)\s*,\s*y\s*=\s*(-?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?)/g;
|
|
let m;
|
|
while ((m = re.exec(text)) !== null) {
|
|
out.push({ id: parseInt(m[1], 10), x: parseFloat(m[2]), y: parseFloat(m[3]) });
|
|
}
|
|
if (out.length) return out;
|
|
|
|
// Fallback: lines of form `index x y` or `index, x, y` (any separator).
|
|
const lines = text.split(/\r?\n/);
|
|
let auto = 1;
|
|
for (const line of lines) {
|
|
const nums = line.match(/-?\d+(?:\.\d+)?(?:[eE][-+]?\d+)?/g);
|
|
if (!nums) continue;
|
|
if (nums.length >= 3) {
|
|
out.push({ id: parseInt(nums[0], 10), x: parseFloat(nums[1]), y: parseFloat(nums[2]) });
|
|
} else if (nums.length === 2) {
|
|
out.push({ id: auto++, x: parseFloat(nums[0]), y: parseFloat(nums[1]) });
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** CSV with optional header. Columns: index,x,y OR x,y. */
|
|
function parseCSV(text) {
|
|
const out = [];
|
|
const lines = text.trim().split(/\r?\n/).filter(Boolean);
|
|
let auto = 1;
|
|
for (const raw of lines) {
|
|
const cols = raw.split(/[,;\t]/).map((s) => s.trim());
|
|
// Skip header rows that don't parse as numbers.
|
|
if (cols.length >= 2 && isNaN(parseFloat(cols[0])) && isNaN(parseFloat(cols[1]))) continue;
|
|
if (cols.length >= 3) {
|
|
const id = parseInt(cols[0], 10);
|
|
const x = parseFloat(cols[1]);
|
|
const y = parseFloat(cols[2]);
|
|
if (!isNaN(x) && !isNaN(y)) out.push({ id: isNaN(id) ? auto++ : id, x, y });
|
|
} else if (cols.length === 2) {
|
|
const x = parseFloat(cols[0]);
|
|
const y = parseFloat(cols[1]);
|
|
if (!isNaN(x) && !isNaN(y)) out.push({ id: auto++, x, y });
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/** JSON: array of objects {id,x,y} OR array of [x,y]. */
|
|
function parseJSON(text) {
|
|
const j = JSON.parse(text);
|
|
if (!Array.isArray(j)) throw new Error("JSON must be a top-level array");
|
|
return j.map((item, i) => {
|
|
if (Array.isArray(item)) {
|
|
return { id: i + 1, x: +item[0], y: +item[1] };
|
|
}
|
|
return {
|
|
id: item.id != null ? +item.id : i + 1,
|
|
x: +item.x,
|
|
y: +item.y,
|
|
};
|
|
});
|
|
}
|
|
|
|
/** Generator — mirrors get_hex_center_points_{rect,para,tria} from hex_cell.scad. */
|
|
function generate({ cellDia, wall, rows, cols, style }) {
|
|
const hex_w = cellDia + 2 * wall;
|
|
const hex_pt = (hex_w / 2) / COS30;
|
|
const rowY = (r) => r * 1.5 * hex_pt;
|
|
const out = [];
|
|
let id = 1;
|
|
|
|
if (style === "rect") {
|
|
for (let r = 0; r < rows; r++) {
|
|
for (let c = 0; c < cols; c++) {
|
|
const x = (r % 2 === 0) ? hex_w * c : 0.5 * hex_w + hex_w * c;
|
|
out.push({ id: id++, x, y: rowY(r) });
|
|
}
|
|
}
|
|
} else if (style === "para") {
|
|
for (let r = 0; r < rows; r++) {
|
|
for (let c = 0; c < cols; c++) {
|
|
const x = r * 0.5 * hex_w + hex_w * c;
|
|
out.push({ id: id++, x, y: rowY(r) });
|
|
}
|
|
}
|
|
} else if (style === "tria") {
|
|
for (let r = 0; r < rows; r++) {
|
|
for (let c = 0; c <= r; c++) {
|
|
const x = r * 0.5 * hex_w - hex_w * c;
|
|
out.push({ id: id++, x, y: rowY(r) });
|
|
}
|
|
}
|
|
} else {
|
|
throw new Error(`unknown style: ${style}`);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
return { parsePaste, parseCSV, parseJSON, generate };
|
|
})();
|