/** * Perçages par pièce pour exports CNC (.nc) et MPR (HOMAC/Woodwop XML-like). * Même jeu de coordonnées que la vue liste / G-code commentaires : origine bas-gauche, X/Y en mm. */ import { buildTopBottomPlanHoleSpecsMm, buildVerticalPanelIxconnectFaceHoleSpecsMm, buildShelfMobileHoleSpecsMm, buildFinishHabillageHoleSpecsMm, buildDoorHingeCupHoleSpecsMm, buildDrawerReinforcementHoleSpecsMm, buildDrawerRenfortPanelIxconnectSpecsMm } from '../../shared/cncPanelHoles.js'; export function collectHoleSpecsForPiece(piece, widthMm, heightMm, partIndex = 0) { const specs = []; function pushHole(params) { specs.push(params); } const m = piece?.machining || {}; const thicknessMm = Math.max(1, Number(piece?.thicknessMm) || 19); const clampX = (v) => Math.max(0, Math.min(widthMm, Number(v) || 0)); const clampY = (v) => Math.max(0, Math.min(heightMm, Number(v) || 0)); if (m.system32?.positions?.length) { const frontX = clampX(37); const backX = clampX(widthMm - 37); const d = Number(m.system32.holeDiameter || 5); const z = Number(m.system32.holeDepth || piece.thicknessMm || 12); const throughHole = Boolean(m.system32.throughHole); for (const pos of m.system32.positions) { const y = clampY(pos); pushHole({ xMm: frontX, yMm: y, diameterMm: d, depthMm: z, type: 'system32', face: 'face', toolMode: throughHole ? 'through' : 'blind' }); pushHole({ xMm: backX, yMm: y, diameterMm: d, depthMm: z, type: 'system32', face: 'face', toolMode: throughHole ? 'through' : 'blind' }); } } if (['dessus', 'fond'].includes(piece.elementType || '')) { for (const spec of buildTopBottomPlanHoleSpecsMm(piece, widthMm, heightMm)) { pushHole({ ...spec, toolMode: spec.face === 'end' ? 'edge' : 'blind' }); } } if (['cotes', 'panneauxInterieurs'].includes(piece.elementType || '')) { for (const spec of buildVerticalPanelIxconnectFaceHoleSpecsMm(piece, widthMm, heightMm)) { pushHole({ ...spec, toolMode: spec.face === 'end' ? 'edge' : 'blind' }); } for (const spec of buildDrawerRenfortPanelIxconnectSpecsMm(piece, widthMm, heightMm, partIndex)) { pushHole({ ...spec, toolMode: spec.face === 'end' ? 'edge' : 'blind' }); } } if (['cotes', 'habillageFinitionGauche', 'habillageFinitionDroite', 'habillageFinitionDessus'].includes(piece.elementType || '')) { for (const spec of buildFinishHabillageHoleSpecsMm(piece, widthMm, heightMm)) { pushHole({ ...spec, toolMode: 'blind' }); } } if (piece.elementType === 'etageres' || piece.elementType === 'rayonFixe') { for (const spec of buildShelfMobileHoleSpecsMm(piece, widthMm, heightMm)) { pushHole({ ...spec, toolMode: spec.face === 'end' ? 'edge' : 'blind' }); } } if (piece.elementType === 'renfortsTiroir') { for (const spec of buildDrawerReinforcementHoleSpecsMm(piece, widthMm, heightMm)) { pushHole({ ...spec, toolMode: 'blind' }); } } if (piece.elementType === 'portes') { for (const spec of buildDoorHingeCupHoleSpecsMm(piece, widthMm, heightMm)) { pushHole({ ...spec, toolMode: 'blind', face: 'face' }); } } if (m.reinforcementFixing) { const rf = m.reinforcementFixing; const dIx = Number(rf.edgeHole?.diameter || 8); const dT = Number(rf.tourillonDiameter || 8); const zIx = Number(rf.edgeHole?.depth || 45); const zT = Number(piece.thicknessMm || 16); const yFromTop = (mm) => clampY(heightMm - Number(mm)); for (const end of ['left', 'right']) { const xEnd = end === 'left' ? 0 : widthMm; if (Number.isFinite(Number(rf.ixconnectAxisFromTopMm))) { pushHole({ xMm: clampX(xEnd), yMm: yFromTop(rf.ixconnectAxisFromTopMm), diameterMm: dIx, depthMm: zIx, type: 'ixconnect', face: 'end', end, toolMode: 'edge' }); } for (const a of rf.tourillonAxesFromTopMm || []) { pushHole({ xMm: clampX(xEnd), yMm: yFromTop(a), diameterMm: dT, depthMm: zT, type: 'tourillon', face: 'end', end, toolMode: 'edge' }); } } } return { specs, thicknessMm }; }