Add split

This commit is contained in:
Халимов Рустам
2026-01-10 16:43:20 +03:00
parent 8789d82064
commit 4af73b00eb
5 changed files with 379 additions and 278 deletions
+84 -31
View File
@@ -1,16 +1,12 @@
import * as THREE from 'three';
import { STLExporter, mergeBufferGeometries } from 'three-stdlib';
import { AppConfig, LayoutSplits, GeneratedPart } from '../types';
import { AppConfig, LayoutSplits, GeneratedPart, Partition } from '../types';
export const calculateParts = (
config: AppConfig,
splits: LayoutSplits
): GeneratedPart[] => {
export const calculateParts = (config: AppConfig, splits: LayoutSplits): GeneratedPart[] => {
const parts: GeneratedPart[] = [];
// Safe Access
const safeX = splits.x || [];
const safeY = splits.y || [];
const safeParts = splits.partitions || {};
const xPoints = [0, ...[...safeX].sort((a, b) => a - b), 1];
const yPoints = [0, ...[...safeY].sort((a, b) => a - b), 1];
@@ -19,20 +15,21 @@ export const calculateParts = (
for (let i = 0; i < xPoints.length - 1; i++) {
for (let j = 0; j < yPoints.length - 1; j++) {
const segmentX = xPoints[i] * config.drawer.width;
const segmentY = yPoints[j] * config.drawer.depth;
const segmentW = (xPoints[i + 1] - xPoints[i]) * config.drawer.width;
const segmentD = (yPoints[j + 1] - yPoints[j]) * config.drawer.depth;
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 realWidth = segmentW - config.printerTolerance;
const realDepth = segmentD - config.printerTolerance;
const realX = segmentX + (config.printerTolerance / 2);
const realY = segmentY + (config.printerTolerance / 2);
// Получаем перегородки для этой ячейки
const internalPartitions = safeParts[`${i}-${j}`] || [];
if (realWidth < 5 || realDepth < 5) {
continue;
}
// Рассчитываем реальные размеры с учетом допуска принтера
const realWidth = rawW - config.printerTolerance;
const realDepth = rawD - config.printerTolerance;
const realX = rawX + (config.printerTolerance / 2);
const realY = rawY + (config.printerTolerance / 2);
if (realWidth < 5 || realDepth < 5) continue;
parts.push({
id: `part-${partCounter}`,
@@ -42,15 +39,18 @@ export const calculateParts = (
height: config.drawer.height,
x: realX,
y: realY,
color: `hsl(${Math.random() * 360}, 70%, 50%)`
color: `hsl(${Math.random() * 360}, 70%, 50%)`,
internalPartitions: internalPartitions
});
partCounter++;
}
}
return parts;
};
// --- GEOMETRY GENERATION ---
// Создает форму скругленного прямоугольника (или обычного)
const createRoundedRectShape = (width: number, height: number, radius: number): THREE.Shape => {
const shape = new THREE.Shape();
const x = -width / 2;
@@ -75,20 +75,25 @@ const createRoundedRectShape = (width: number, height: number, radius: number):
shape.quadraticCurveTo(x, y, x, y + r);
}
return shape;
}
};
// Генерирует геометрию ячейки С ПЕРЕГОРОДКАМИ
export const createBinGeometry = (
width: number,
depth: number,
height: number,
thickness: number,
radius: number = 0
radius: number = 0,
partitions: Partition[] = []
): THREE.BufferGeometry => {
const geometries: THREE.BufferGeometry[] = [];
// 1. ОСНОВНАЯ КОРОБКА (Дно + Стенки)
const floorShape = createRoundedRectShape(width, depth, radius);
const floorGeo = new THREE.ExtrudeGeometry(floorShape, {
depth: thickness, bevelEnabled: false, curveSegments: 16
});
const floorGeo = new THREE.ExtrudeGeometry(floorShape, { depth: thickness, bevelEnabled: false, curveSegments: 12 });
floorGeo.rotateX(-Math.PI / 2);
geometries.push(floorGeo);
const outerShape = createRoundedRectShape(width, depth, radius);
const innerRadius = Math.max(0, radius - thickness);
@@ -101,19 +106,67 @@ export const createBinGeometry = (
}
const wallHeight = height - thickness;
const wallGeo = new THREE.ExtrudeGeometry(outerShape, {
depth: wallHeight, bevelEnabled: false, curveSegments: 16
});
const wallGeo = new THREE.ExtrudeGeometry(outerShape, { depth: wallHeight, bevelEnabled: false, curveSegments: 12 });
wallGeo.rotateX(-Math.PI / 2);
wallGeo.translate(0, thickness, 0);
geometries.push(wallGeo);
const merged = mergeBufferGeometries([floorGeo, wallGeo]);
// 2. ВНУТРЕННИЕ ПЕРЕГОРОДКИ
// Мы создаем их внутри внутреннего пространства (innerWidth/innerDepth)
partitions.forEach(p => {
// Размеры перегородки
let pWidth = 0;
let pDepth = 0;
// Позиция центра перегородки относительно центра ящика
let pX = 0;
let pY = 0; // (это Z в 3D)
if (p.axis === 'x') {
// Вертикальная палка (делит ширину)
pWidth = thickness;
// Длина палки равна внутренней глубине ящика
pDepth = innerDepth;
// Смещение: p.offset (0..1) переводим в координаты.
// innerLeft = -innerWidth/2. Position = innerLeft + (innerWidth * offset)
pX = (-innerWidth / 2) + (innerWidth * p.offset);
pY = 0; // По центру глубины
} else {
// Горизонтальная палка (делит глубину)
pWidth = innerWidth;
pDepth = thickness;
pX = 0; // По центру ширины
pY = (-innerDepth / 2) + (innerDepth * p.offset);
}
// Форма перегородки (скругленная или нет)
// Если скругленная, радиус берем такой же как у основной стенки, но не больше половины толщины
const pRadius = p.rounded ? Math.min(radius, thickness / 1.5) : 0;
const partShape = createRoundedRectShape(pWidth, pDepth, pRadius);
const partGeo = new THREE.ExtrudeGeometry(partShape, {
depth: p.height, // Высота перегородки (может отличаться от основной)
bevelEnabled: false,
curveSegments: 8
});
partGeo.rotateX(-Math.PI / 2);
// Поднимаем на толщину дна
partGeo.translate(pX, thickness, pY);
geometries.push(partGeo);
});
// 3. СЛИЯНИЕ
const merged = mergeBufferGeometries(geometries);
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 });