Added split in to cell
This commit is contained in:
@@ -2,16 +2,12 @@ import * as THREE from 'three';
|
||||
import { STLExporter, mergeBufferGeometries } from 'three-stdlib';
|
||||
import { AppConfig, LayoutSplits, GeneratedPart } from '../types';
|
||||
|
||||
/**
|
||||
* Calculates the final list of bins based on layout.
|
||||
*/
|
||||
export const calculateParts = (
|
||||
config: AppConfig,
|
||||
splits: LayoutSplits
|
||||
): GeneratedPart[] => {
|
||||
const parts: GeneratedPart[] = [];
|
||||
|
||||
// Сортируем линии разреза
|
||||
const xPoints = [0, ...[...splits.x].sort((a, b) => a - b), 1];
|
||||
const yPoints = [0, ...[...splits.y].sort((a, b) => a - b), 1];
|
||||
|
||||
@@ -20,59 +16,69 @@ 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;
|
||||
|
||||
// Применяем зазор (Tolerance)
|
||||
const realWidth = segmentW - config.printerTolerance;
|
||||
const realDepth = segmentD - config.printerTolerance;
|
||||
const realX = segmentX + (config.printerTolerance / 2);
|
||||
const realY = segmentY + (config.printerTolerance / 2);
|
||||
// Проверяем, есть ли разделение для этой ячейки
|
||||
const subdiv = splits.subdivisions?.[`${i}-${j}`] || { rows: 1, cols: 1 };
|
||||
|
||||
// Вычисляем размер одной "под-ячейки"
|
||||
// Делим общую ширину на кол-во колонок
|
||||
const subCellWidth = rawW / subdiv.cols;
|
||||
const subCellDepth = rawD / subdiv.rows;
|
||||
|
||||
// Игнорируем слишком мелкие детали
|
||||
if (realWidth < 5 || realDepth < 5) {
|
||||
continue;
|
||||
// Генерируем под-ячейки
|
||||
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);
|
||||
|
||||
// Применяем Tolerance (зазор) к каждой микро-ячейке
|
||||
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;
|
||||
|
||||
parts.push({
|
||||
id: `part-${partCounter}`,
|
||||
name: `Ячейка ${i+1}-${j+1}` + (subdiv.rows > 1 || subdiv.cols > 1 ? ` (${r+1}x${c+1})` : ''),
|
||||
width: realWidth,
|
||||
depth: realDepth,
|
||||
height: config.drawer.height,
|
||||
x: realX,
|
||||
y: realY,
|
||||
color: `hsl(${Math.random() * 360}, 70%, 50%)`
|
||||
});
|
||||
partCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
parts.push({
|
||||
id: `part-${partCounter}`,
|
||||
name: `Ячейка ${i+1}-${j+1}`,
|
||||
width: realWidth,
|
||||
depth: realDepth,
|
||||
height: config.drawer.height,
|
||||
x: realX,
|
||||
y: realY,
|
||||
color: `hsl(${Math.random() * 360}, 70%, 50%)`
|
||||
});
|
||||
partCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
return parts;
|
||||
};
|
||||
|
||||
/**
|
||||
* Создает 2D форму прямоугольника со скругленными краями
|
||||
*/
|
||||
// ... Остальной код (createBinGeometry, exportSTL) остается без изменений ...
|
||||
// (Копируй функции 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) {
|
||||
// Обычный прямоугольник (если радиус 0)
|
||||
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);
|
||||
@@ -83,13 +89,9 @@ const createRoundedRectShape = (width: number, height: number, radius: number):
|
||||
shape.lineTo(x + r, y);
|
||||
shape.quadraticCurveTo(x, y, x, y + r);
|
||||
}
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Генерирует 3D геометрию ящика
|
||||
*/
|
||||
export const createBinGeometry = (
|
||||
width: number,
|
||||
depth: number,
|
||||
@@ -97,25 +99,15 @@ export const createBinGeometry = (
|
||||
thickness: number,
|
||||
radius: number = 0
|
||||
): THREE.BufferGeometry => {
|
||||
|
||||
// 1. ГЕОМЕТРИЯ ДНА (Сплошная)
|
||||
const floorShape = createRoundedRectShape(width, depth, radius);
|
||||
|
||||
const floorGeo = new THREE.ExtrudeGeometry(floorShape, {
|
||||
depth: thickness, // Выдавливаем на толщину дна
|
||||
depth: thickness,
|
||||
bevelEnabled: false,
|
||||
curveSegments: 16 // Количество сегментов на скруглениях
|
||||
curveSegments: 16
|
||||
});
|
||||
|
||||
// Extrude выдавливает по оси Z. Нам нужно повернуть, чтобы "глубина" стала "высотой" (Y).
|
||||
// Поворот на -90 градусов вокруг X кладет Z на Y.
|
||||
floorGeo.rotateX(-Math.PI / 2);
|
||||
// Теперь дно занимает пространство от Y=0 до Y=thickness.
|
||||
|
||||
// 2. ГЕОМЕТРИЯ СТЕНОК (С дыркой)
|
||||
const outerShape = createRoundedRectShape(width, depth, radius);
|
||||
|
||||
// Вырезаем внутреннюю часть
|
||||
const innerRadius = Math.max(0, radius - thickness);
|
||||
const innerWidth = width - (2 * thickness);
|
||||
const innerDepth = depth - (2 * thickness);
|
||||
@@ -125,32 +117,18 @@ export const createBinGeometry = (
|
||||
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);
|
||||
|
||||
// Сейчас стенки тоже начинаются с Y=0.
|
||||
// Нам нужно поднять их НАД дном.
|
||||
wallGeo.translate(0, thickness, 0);
|
||||
|
||||
// Теперь стенки занимают пространство от Y=thickness до Y=height.
|
||||
|
||||
// 3. ОБЪЕДИНЕНИЕ
|
||||
// Сливаем две геометрии в одну. Слайсеры поймут это как единый объект,
|
||||
// так как поверхности идеально соприкасаются.
|
||||
const merged = mergeBufferGeometries([floorGeo, wallGeo]);
|
||||
|
||||
// Центрирование не нужно, так как createRoundedRectShape строит форму вокруг (0,0) по X и Z.
|
||||
// А по Y мы выстроили от 0 вверх.
|
||||
// Pivot point (опорная точка) осталась внизу в центре (0,0,0), что идеально для позиционирования.
|
||||
if (merged) merged.computeVertexNormals();
|
||||
|
||||
return merged || new THREE.BoxGeometry(1, 1, 1);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user