Files
BoxGenerator/src/services/geometryGenerator.ts
Халимов Рустам 929e071ea7 Try repeat
2025-12-27 23:46:13 +03:00

155 lines
5.3 KiB
TypeScript

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();
};