import * as THREE from 'three'; import { STLExporter, mergeBufferGeometries } from 'three-stdlib'; import { AppConfig, LayoutSplits, GeneratedPart } from '../types'; export const calculateParts = ( config: AppConfig, splits: LayoutSplits ): GeneratedPart[] => { const parts: GeneratedPart[] = []; // Безопасное чтение данных const safeX = splits?.x || []; const safeY = splits?.y || []; const safeSub = splits?.subdivisions || {}; const xPoints = [0, ...[...safeX].sort((a, b) => a - b), 1]; const yPoints = [0, ...[...safeY].sort((a, b) => a - b), 1]; let partCounter = 1; for (let i = 0; i < xPoints.length - 1; i++) { for (let j = 0; j < yPoints.length - 1; j++) { const rawX = xPoints[i] * config.drawer.width; const rawY = yPoints[j] * config.drawer.depth; const rawW = (xPoints[i + 1] - xPoints[i]) * config.drawer.width; const rawD = (yPoints[j + 1] - yPoints[j]) * config.drawer.depth; // Получаем настройки деления const subdiv = safeSub[`${i}-${j}`] || { rows: 1, cols: 1 }; // Размеры под-ячейки const subCellWidth = rawW / subdiv.cols; const subCellDepth = rawD / subdiv.rows; // Генерируем под-ячейки for (let r = 0; r < subdiv.rows; r++) { for (let c = 0; c < subdiv.cols; c++) { const subX = rawX + (c * subCellWidth); const subY = rawY + (r * subCellDepth); const realWidth = subCellWidth - config.printerTolerance; const realDepth = subCellDepth - config.printerTolerance; const realX = subX + (config.printerTolerance / 2); const realY = subY + (config.printerTolerance / 2); if (realWidth < 5 || realDepth < 5) continue; // Имя: если деление, добавляем суффикс let partName = `Ячейка ${i+1}-${j+1}`; if (subdiv.rows > 1 || subdiv.cols > 1) { partName += ` (${r+1}-${c+1})`; } parts.push({ id: `part-${partCounter}`, name: partName, width: realWidth, depth: realDepth, height: config.drawer.height, x: realX, y: realY, color: `hsl(${Math.random() * 360}, 70%, 50%)` }); partCounter++; } } } } return parts; }; // ... Остальной код (createRoundedRectShape, createBinGeometry) как в работающей версии ... const createRoundedRectShape = (width: number, height: number, radius: number): THREE.Shape => { const shape = new THREE.Shape(); const x = -width / 2; const y = -height / 2; const r = Math.min(radius, width / 2, height / 2); if (r <= 0.1) { shape.moveTo(x, y); shape.lineTo(x + width, y); shape.lineTo(x + width, y + height); shape.lineTo(x, y + height); shape.lineTo(x, y); } else { shape.moveTo(x, y + r); shape.lineTo(x, y + height - r); shape.quadraticCurveTo(x, y + height, x + r, y + height); shape.lineTo(x + width - r, y + height); shape.quadraticCurveTo(x + width, y + height, x + width, y + height - r); shape.lineTo(x + width, y + r); shape.quadraticCurveTo(x + width, y, x + width - r, y); shape.lineTo(x + r, y); shape.quadraticCurveTo(x, y, x, y + r); } return shape; } export const createBinGeometry = ( width: number, depth: number, height: number, thickness: number, radius: number = 0 ): THREE.BufferGeometry => { const floorShape = createRoundedRectShape(width, depth, radius); const floorGeo = new THREE.ExtrudeGeometry(floorShape, { depth: thickness, bevelEnabled: false, curveSegments: 16 }); floorGeo.rotateX(-Math.PI / 2); const outerShape = createRoundedRectShape(width, depth, radius); const innerRadius = Math.max(0, radius - thickness); const innerWidth = width - (2 * thickness); const innerDepth = depth - (2 * thickness); if (innerWidth > 0 && innerDepth > 0) { const innerHole = createRoundedRectShape(innerWidth, innerDepth, innerRadius); outerShape.holes.push(innerHole); } const wallHeight = height - thickness; const wallGeo = new THREE.ExtrudeGeometry(outerShape, { depth: wallHeight, bevelEnabled: false, curveSegments: 16 }); wallGeo.rotateX(-Math.PI / 2); wallGeo.translate(0, thickness, 0); const merged = mergeBufferGeometries([floorGeo, wallGeo]); if (merged) merged.computeVertexNormals(); return merged || new THREE.BoxGeometry(1, 1, 1); }; export const generateSTL = (mesh: THREE.Object3D): Uint8Array | string => { const exporter = new STLExporter(); const result = exporter.parse(mesh, { binary: true }); if (result instanceof DataView) { return new Uint8Array(result.buffer, result.byteOffset, result.byteLength); } return result as string; }; export const exportSTL = (mesh: THREE.Object3D, filename: string) => { const result = generateSTL(mesh); const blob = new Blob([result], { type: 'application/octet-stream' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; link.click(); };