2
This commit is contained in:
@@ -2,45 +2,6 @@ import * as THREE from 'three';
|
||||
import { STLExporter, mergeBufferGeometries } from 'three-stdlib';
|
||||
import { AppConfig, LayoutSplits, GeneratedPart, Partition } from '../types';
|
||||
|
||||
type Limits = { min: number; max: number };
|
||||
type LimitMap = Record<string, Limits>;
|
||||
|
||||
// --- SOLVER: Идентичен тому, что в LayoutStep.tsx ---
|
||||
// Гарантирует, что 3D модель будет выглядеть точно так же, как 2D макет
|
||||
const solveWallLimits = (partitions: Partition[]): LimitMap => {
|
||||
const limits: LimitMap = {};
|
||||
// 1. Сброс границ
|
||||
partitions.forEach(p => { limits[p.id] = { min: 0, max: 1 }; });
|
||||
|
||||
// 2. Итеративное решение коллизий (4 прохода для надежности)
|
||||
for (let pass = 0; pass < 4; pass++) {
|
||||
partitions.forEach(target => {
|
||||
let newMin = 0;
|
||||
let newMax = 1;
|
||||
const center = target.offset;
|
||||
|
||||
partitions.forEach(obstacle => {
|
||||
if (target.id === obstacle.id || target.axis === obstacle.axis) return;
|
||||
|
||||
// Берем актуальные границы препятствия
|
||||
const obsMin = limits[obstacle.id].min;
|
||||
const obsMax = limits[obstacle.id].max;
|
||||
|
||||
// ВАЖНО: Используем тот же допуск (EPSILON), что и в 2D редакторе
|
||||
const EPS = 0.002;
|
||||
|
||||
// Проверяем пересечение
|
||||
if (target.offset >= obsMin - EPS && target.offset <= obsMax + EPS) {
|
||||
if (obstacle.offset < center) newMin = Math.max(newMin, obstacle.offset);
|
||||
else if (obstacle.offset > center) newMax = Math.min(newMax, obstacle.offset);
|
||||
}
|
||||
});
|
||||
limits[target.id] = { min: newMin, max: newMax };
|
||||
});
|
||||
}
|
||||
return limits;
|
||||
};
|
||||
|
||||
export const calculateParts = (config: AppConfig, splits: LayoutSplits): GeneratedPart[] => {
|
||||
const parts: GeneratedPart[] = [];
|
||||
const safeX = Array.isArray(splits?.x) ? splits.x : [];
|
||||
@@ -61,7 +22,6 @@ export const calculateParts = (config: AppConfig, splits: LayoutSplits): Generat
|
||||
|
||||
const rawX = xPoints[i] * config.drawer.width;
|
||||
const rawY = yPoints[j] * config.drawer.depth;
|
||||
|
||||
const internalPartitions = safeParts[`${i}-${j}`] || [];
|
||||
|
||||
const realWidth = rawW - config.printerTolerance;
|
||||
@@ -150,13 +110,12 @@ export const createBinGeometry = (
|
||||
wallGeo.translate(0, thickness, 0);
|
||||
geometries.push(wallGeo);
|
||||
|
||||
// ВНУТРЕННИЕ ПЕРЕГОРОДКИ (С SOLVER'ом)
|
||||
const limitMap = solveWallLimits(partitions);
|
||||
|
||||
// ВНУТРЕННИЕ ПЕРЕГОРОДКИ (СТРОГО ПО ДАННЫМ, БЕЗ SOLVER)
|
||||
partitions.forEach(p => {
|
||||
// Используем рассчитанные границы, а не старые сохраненные
|
||||
const { min: pMin, max: pMax } = limitMap[p.id];
|
||||
|
||||
// Берем данные напрямую. Если в 2D нарисовано от 0.2 до 0.8, тут будет 0.2 до 0.8.
|
||||
const pMin = p.min ?? 0;
|
||||
const pMax = p.max ?? 1;
|
||||
|
||||
if (pMax - pMin < 0.01) return;
|
||||
|
||||
const lengthRatio = pMax - pMin;
|
||||
@@ -182,20 +141,23 @@ export const createBinGeometry = (
|
||||
partGeo.translate(pX, thickness, pY);
|
||||
geometries.push(partGeo);
|
||||
|
||||
// СКРУГЛЕНИЯ
|
||||
// СКРУГЛЕНИЯ (Fillets)
|
||||
if (p.rounded && radius > 1) {
|
||||
const filletR = Math.min(radius, 5);
|
||||
const filletShape = createConcaveFilletShape(filletR);
|
||||
|
||||
// Функция высоты соседа (чтобы скругление не висело в воздухе)
|
||||
|
||||
// Функция проверки высоты соседа (простая проверка на пересечение)
|
||||
const getNeighborHeight = (pos: number) => {
|
||||
if (pos < 0.001 || pos > 0.999) return height; // Край ящика = полная высота
|
||||
if (pos < 0.001 || pos > 0.999) return height; // Край ящика
|
||||
|
||||
const neighbor = partitions.find(n => {
|
||||
if (n.axis === p.axis) return false;
|
||||
// Проверка на пересечение координат
|
||||
const nLims = limitMap[n.id]; // Берем актуальные границы соседа
|
||||
return Math.abs(n.offset - pos) < 0.002 && p.offset >= nLims.min && p.offset <= nLims.max;
|
||||
if (n.axis === p.axis) return false; // Перпендикуляр
|
||||
const nMin = n.min ?? 0;
|
||||
const nMax = n.max ?? 1;
|
||||
// Совпадает ли позиция?
|
||||
if (Math.abs(n.offset - pos) > 0.002) return false;
|
||||
// Перекрывает ли?
|
||||
return p.offset > nMin && p.offset < nMax;
|
||||
});
|
||||
return neighbor ? neighbor.height : 0;
|
||||
};
|
||||
@@ -204,7 +166,7 @@ export const createBinGeometry = (
|
||||
const hEnd = Math.min(p.height, getNeighborHeight(pMax));
|
||||
|
||||
const addFillet = (x: number, y: number, rotY: number, h: number) => {
|
||||
if (h <= 1) return;
|
||||
if (h <= 1) return;
|
||||
const geo = new THREE.ExtrudeGeometry(filletShape, { depth: h, bevelEnabled: false });
|
||||
geo.rotateX(-Math.PI / 2);
|
||||
geo.rotateY(rotY);
|
||||
@@ -220,7 +182,6 @@ export const createBinGeometry = (
|
||||
|
||||
addFillet(pX - t, topY, Math.PI, hStart);
|
||||
addFillet(pX + t, topY, -Math.PI / 2, hStart);
|
||||
|
||||
addFillet(pX - t, botY, Math.PI / 2, hEnd);
|
||||
addFillet(pX + t, botY, 0, hEnd);
|
||||
} else {
|
||||
@@ -229,7 +190,6 @@ export const createBinGeometry = (
|
||||
|
||||
addFillet(leftX, pY - t, 0, hStart);
|
||||
addFillet(leftX, pY + t, -Math.PI / 2, hStart);
|
||||
|
||||
addFillet(rightX, pY - t, Math.PI / 2, hEnd);
|
||||
addFillet(rightX, pY + t, Math.PI, hEnd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user