Co-authored-by: Cursor <[email protected]>
This commit is contained in:
lejay
2026-05-28 07:20:38 +02:00
co-authored by Cursor
parent 6923c319a1
commit 6c0d7e47b8
81 changed files with 21780 additions and 2986 deletions
+110
View File
@@ -0,0 +1,110 @@
import { generateCuttingList } from './cuttingEngine.js';
import { collectHoleSpecsForPiece } from './holeSpecifications.js';
import { GROOVE, getBackGroovePlanOrientation } from '../../shared/manufacturing.js';
function toSafeNumber(value, fallback = 0) {
const n = Number(value);
return Number.isFinite(n) ? n : fallback;
}
function toLabel(value) {
return String(value || '')
.replace(/[^a-zA-Z0-9_\-]+/g, '_')
.replace(/_+/g, '_')
.replace(/^_+|_+$/g, '')
.toUpperCase();
}
function emitPanelOutline(lines, widthMm, heightMm, safeZ, cutZ, feed) {
lines.push(`G0 Z${safeZ.toFixed(3)}`);
lines.push('G0 X0.000 Y0.000');
lines.push(`G1 Z${cutZ.toFixed(3)} F${Math.round(feed)}`);
lines.push(`G1 X${widthMm.toFixed(3)} Y0.000`);
lines.push(`G1 X${widthMm.toFixed(3)} Y${heightMm.toFixed(3)}`);
lines.push(`G1 X0.000 Y${heightMm.toFixed(3)}`);
lines.push('G1 X0.000 Y0.000');
lines.push(`G0 Z${safeZ.toFixed(3)}`);
}
function emitHole(lines, { xMm, yMm, diameterMm = 5, depthMm = 10, type = 'generic', face = 'face', end = null }) {
let tail = `TYPE=${String(type)} FACE=${face}`;
if (end) tail += ` END=${end}`;
lines.push(
`; HOLE X=${Number(xMm).toFixed(3)} Y=${Number(yMm).toFixed(3)} D=${Number(diameterMm).toFixed(3)} Z=${Number(depthMm).toFixed(3)} ${tail}`
);
}
function emitPieceHoleComments(lines, piece, widthMm, heightMm, partIndex = 0) {
const { specs, thicknessMm: _tm } = collectHoleSpecsForPiece(piece, widthMm, heightMm, partIndex);
for (const spec of specs) {
emitHole(lines, spec);
}
const m = piece?.machining || {};
const orient = piece?.elementType ? getBackGroovePlanOrientation(piece.elementType) : null;
if (m.groove && orient) {
const gw = Number(m.groove.width ?? GROOVE.back.width);
const gd = Number(m.groove.depth ?? GROOVE.back.depth);
const ax = Number(m.groove.axisFromPanelEdge ?? m.groove.axisFromBackEdge ?? GROOVE.back.axisFromPanelEdge);
lines.push(`; GROOVE BACK W=${gw.toFixed(3)} D=${gd.toFixed(3)} AXIS=${ax.toFixed(3)} ORIENT=${orient}`);
}
}
export function generateCncProgram(config, modules = [], colors = {}) {
const cuttingList = generateCuttingList(config, colors, modules);
const safeZ = 10;
const cutDepth = 3;
const feedCut = 1800;
const lines = [
'%',
'; APEX CNC ISO (G-code) - v1',
`; Generated: ${new Date().toISOString()}`,
`; Config: ${config.widthMm}x${config.heightMm}x${config.depthMm} mm`,
'G21 ; metric units',
'G90 ; absolute coordinates',
'G17 ; XY plane',
`G0 Z${safeZ.toFixed(3)}`
];
let programPartCount = 0;
for (const piece of cuttingList.pieces || []) {
const widthMm = toSafeNumber(piece.rawCutWidthMm, piece.widthMm);
const heightMm = toSafeNumber(piece.rawCutHeightMm, piece.heightMm);
const qty = Math.max(1, Math.round(toSafeNumber(piece.quantity, 1)));
const thickness = toSafeNumber(piece.thicknessMm, 19);
const pieceName = toLabel(piece.description || piece.elementType || 'PIECE');
const targetDepth = -Math.min(cutDepth, Math.max(1, thickness * 0.25));
for (let i = 0; i < qty; i++) {
programPartCount += 1;
lines.push('');
lines.push(`; --- PART ${programPartCount}: ${pieceName} #${i + 1} ---`);
lines.push(`; SIZE_RAW: X=${widthMm.toFixed(3)} Y=${heightMm.toFixed(3)} T=${thickness.toFixed(3)}`);
emitPieceHoleComments(lines, piece, widthMm, heightMm, i);
emitPanelOutline(lines, widthMm, heightMm, safeZ, targetDepth, feedCut);
}
}
lines.push('');
lines.push(`G0 Z${safeZ.toFixed(3)}`);
lines.push('G0 X0.000 Y0.000');
lines.push('M30');
lines.push('%');
return {
fileName: 'apex-program.nc',
mimeType: 'text/plain; charset=utf-8',
content: lines.join('\n'),
metadata: {
generatedAt: new Date().toISOString(),
parts: programPartCount
}
};
}
export default {
generateCncProgram
};