/** * Plan Generator - Generate 2D assembly and parts plans in SVG format * Also handles PDF export */ // Constants for drawing const DRAWING_SCALE = 0.1; // 1mm = 0.1px const MARGIN = 50; const LABEL_OFFSET = 15; const ARROW_SIZE = 8; /** * Generate SVG for assembly view (front elevation) */ export function generateAssemblyPlan(config, modules = []) { const { widthMm, heightMm, plinthHeightMm, doorType, installationType } = config; const bodyHeight = heightMm - plinthHeightMm; const svgWidth = widthMm * DRAWING_SCALE + MARGIN * 2; const svgHeight = heightMm * DRAWING_SCALE + MARGIN * 2; let svg = ``; svg += ``; // Title svg += `Vue de face - Élévation`; const offsetX = MARGIN; const offsetY = MARGIN; // Plinth const plinthY = offsetY + bodyHeight * DRAWING_SCALE; svg += ``; // Body svg += ``; // Module separators if (modules.length > 0) { let currentX = offsetX; for (let i = 0; i < modules.length - 1; i++) { currentX += modules[i].widthMm * DRAWING_SCALE; svg += ``; } } // Doors if (doorType === 'sliding') { const panelCount = config.slidingPanelsCount || 2; const panelWidth = (widthMm / panelCount) * DRAWING_SCALE; for (let i = 0; i < panelCount; i++) { svg += ``; } } else { const blockCount = modules.length > 0 ? modules.length : (config.blocks?.length || 2); let currentX = offsetX; const items = modules.length > 0 ? modules : (config.blocks || []); for (let i = 0; i < blockCount; i++) { const blockWidth = items[i]?.widthMm || (widthMm / blockCount); svg += ``; currentX += blockWidth * DRAWING_SCALE; } } // Dimension lines svg += generateDimensionLine(offsetX, plinthY + plinthHeightMm * DRAWING_SCALE + 20, widthMm * DRAWING_SCALE, `${widthMm} mm`, 'horizontal'); svg += generateDimensionLine(offsetX - 20, offsetY, bodyHeight * DRAWING_SCALE, `${bodyHeight} mm`, 'vertical'); svg += generateDimensionLine(offsetX - 40, offsetY, heightMm * DRAWING_SCALE, `${heightMm} mm (total)`, 'vertical'); svg += ''; return svg; } /** * Generate SVG for side view */ export function generateSidePlan(config) { const { heightMm, depthMm, plinthHeightMm } = config; const bodyHeight = heightMm - plinthHeightMm; const svgWidth = depthMm * DRAWING_SCALE + MARGIN * 2; const svgHeight = heightMm * DRAWING_SCALE + MARGIN * 2; let svg = ``; svg += ``; svg += `Vue de côté`; const offsetX = MARGIN; const offsetY = MARGIN; // Plinth const plinthY = offsetY + bodyHeight * DRAWING_SCALE; svg += ``; // Body svg += ``; // Dimension svg += generateDimensionLine(offsetX, plinthY + plinthHeightMm * DRAWING_SCALE + 20, depthMm * DRAWING_SCALE, `${depthMm} mm`, 'horizontal'); svg += ''; return svg; } /** * Generate SVG for top view */ export function generateTopPlan(config, modules = []) { const { widthMm, depthMm } = config; const svgWidth = widthMm * DRAWING_SCALE + MARGIN * 2; const svgHeight = depthMm * DRAWING_SCALE + MARGIN * 2; let svg = ``; svg += ``; svg += `Vue de dessus`; const offsetX = MARGIN; const offsetY = MARGIN; // Body outline svg += ``; // Module separators and labels if (modules.length > 0) { let currentX = offsetX; for (let i = 0; i < modules.length; i++) { const module = modules[i]; const moduleWidth = module.widthMm * DRAWING_SCALE; // Label svg += `${module.moduleCode || `M${i + 1}`}`; // Separator if (i < modules.length - 1) { currentX += moduleWidth; svg += ``; } } } svg += ''; return svg; } /** * Generate a dimension line with arrows and text */ function generateDimensionLine(x, y, length, label, direction = 'horizontal') { let svg = ''; if (direction === 'horizontal') { // Line svg += ``; // Arrows svg += ``; svg += ``; // Label svg += `${label}`; } else { // Line svg += ``; // Arrows svg += ``; svg += ``; // Label (rotated) svg += `${label}`; } return svg; } /** * Generate all plans for a configuration */ export function generateAllPlans(config, modules = [], colors = {}) { return { assembly: generateAssemblyPlan(config, modules), side: generateSidePlan(config), top: generateTopPlan(config, modules), metadata: { generatedAt: new Date().toISOString(), config: { widthMm: config.widthMm, heightMm: config.heightMm, depthMm: config.depthMm, installationType: config.installationType, doorType: config.doorType, moduleCount: modules.length } } }; } /** * Generate HTML page with all plans for PDF export */ export function generatePDFHtml(config, modules = [], cuttingList = null) { const plans = generateAllPlans(config, modules); let html = ` Plans Armoire

Plans de fabrication - Armoire sur mesure

Généré le ${new Date().toLocaleDateString('fr-FR')} à ${new Date().toLocaleTimeString('fr-FR')}

Spécifications

Largeur:${config.widthMm} mm
Hauteur:${config.heightMm} mm
Profondeur:${config.depthMm} mm
Plinthe:${config.plinthHeightMm} mm
Type:${config.installationType === 'niche' ? 'Encastrée' : 'Libre'}
Portes:${config.doorType === 'sliding' ? 'Coulissantes' : 'Battantes'}
Vue de face (Élévation)
${plans.assembly}
Vue de côté
${plans.side}
Vue de dessus
${plans.top}
`; // Cutting list page if (cuttingList) { html += `

Liste de débitage

Résumé

Nombre de pièces:${cuttingList.summary.totalPieces}
Surface totale:${cuttingList.summary.totalSurfaceM2.toFixed(2)} m²
Chant plaqué:${cuttingList.summary.edgeBandingMl} ml
Finitions:${cuttingList.summary.finishCount}
`; for (const group of cuttingList.byFinish) { html += `

Finition: ${group.finishLabel}

`; for (const piece of group.pieces) { html += ` `; } html += `
Description Largeur Hauteur Ép. Qté Surface
${piece.description} ${piece.widthMm} mm ${piece.heightMm} mm ${piece.thicknessMm} mm ${piece.quantity} ${piece.surfaceM2.toFixed(3)} m²
Sous-total ${group.pieceCount} ${group.totalSurfaceM2.toFixed(2)} m²
`; } html += `
`; } html += ` `; return html; } export default { generateAssemblyPlan, generateSidePlan, generateTopPlan, generateAllPlans, generatePDFHtml };