Height limit
This commit is contained in:
@@ -2,6 +2,37 @@ import * as THREE from 'three';
|
|||||||
import { STLExporter, mergeBufferGeometries } from 'three-stdlib';
|
import { STLExporter, mergeBufferGeometries } from 'three-stdlib';
|
||||||
import { AppConfig, LayoutSplits, GeneratedPart, Partition } from '../types';
|
import { AppConfig, LayoutSplits, GeneratedPart, Partition } from '../types';
|
||||||
|
|
||||||
|
type Limits = { min: number; max: number };
|
||||||
|
type LimitMap = Record<string, Limits>;
|
||||||
|
|
||||||
|
// --- SOLVER ---
|
||||||
|
const solveWallLimits = (partitions: Partition[]): LimitMap => {
|
||||||
|
const limits: LimitMap = {};
|
||||||
|
partitions.forEach(p => { limits[p.id] = { min: 0, max: 1 }; });
|
||||||
|
|
||||||
|
for (let pass = 0; pass < 3; 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;
|
||||||
|
|
||||||
|
if (target.offset > obsMin && target.offset < obsMax) {
|
||||||
|
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[] => {
|
export const calculateParts = (config: AppConfig, splits: LayoutSplits): GeneratedPart[] => {
|
||||||
const parts: GeneratedPart[] = [];
|
const parts: GeneratedPart[] = [];
|
||||||
const safeX = Array.isArray(splits?.x) ? splits.x : [];
|
const safeX = Array.isArray(splits?.x) ? splits.x : [];
|
||||||
@@ -111,11 +142,10 @@ export const createBinGeometry = (
|
|||||||
geometries.push(wallGeo);
|
geometries.push(wallGeo);
|
||||||
|
|
||||||
// ВНУТРЕННИЕ ПЕРЕГОРОДКИ
|
// ВНУТРЕННИЕ ПЕРЕГОРОДКИ
|
||||||
// Мы просто верим сохраненным данным (p.min/p.max). Они должны быть корректны при создании.
|
const limitMap = solveWallLimits(partitions);
|
||||||
partitions.forEach(p => {
|
|
||||||
const pMin = p.min ?? 0;
|
|
||||||
const pMax = p.max ?? 1;
|
|
||||||
|
|
||||||
|
partitions.forEach(p => {
|
||||||
|
const { min: pMin, max: pMax } = limitMap[p.id];
|
||||||
if (pMax - pMin < 0.01) return;
|
if (pMax - pMin < 0.01) return;
|
||||||
|
|
||||||
const lengthRatio = pMax - pMin;
|
const lengthRatio = pMax - pMin;
|
||||||
@@ -141,38 +171,77 @@ export const createBinGeometry = (
|
|||||||
partGeo.translate(pX, thickness, pY);
|
partGeo.translate(pX, thickness, pY);
|
||||||
geometries.push(partGeo);
|
geometries.push(partGeo);
|
||||||
|
|
||||||
// СКРУГЛЕНИЯ
|
// --- ДОБАВЛЕНИЕ ГАЛТЕЛЕЙ (FILLETS) ---
|
||||||
if (p.rounded && radius > 1) {
|
if (p.rounded && radius > 1) {
|
||||||
const filletR = Math.min(radius, 5);
|
const filletR = Math.min(radius, 5);
|
||||||
const filletShape = createConcaveFilletShape(filletR);
|
const filletShape = createConcaveFilletShape(filletR);
|
||||||
const filletExtrude = { depth: p.height, bevelEnabled: false };
|
|
||||||
|
|
||||||
const addFillet = (x: number, y: number, rotY: number) => {
|
// ФУНКЦИЯ ДЛЯ ПОИСКА ВЫСОТЫ СОСЕДА
|
||||||
const geo = new THREE.ExtrudeGeometry(filletShape, filletExtrude);
|
const getNeighborHeight = (pos: number) => {
|
||||||
|
// Если это край ящика (0 или 1), то высота равна высоте ящика
|
||||||
|
if (pos < 0.001 || pos > 0.999) return height;
|
||||||
|
|
||||||
|
// Ищем внутреннюю стенку в этой точке
|
||||||
|
const neighbor = partitions.find(n => {
|
||||||
|
if (n.axis === p.axis) return false; // Должна быть перпендикулярна
|
||||||
|
|
||||||
|
// Соседка должна находиться в точке pos (по своей оси смещения)
|
||||||
|
if (Math.abs(n.offset - pos) > 0.001) return false;
|
||||||
|
|
||||||
|
// И соседка должна перекрывать нашу стенку (по своей длине)
|
||||||
|
const nLims = limitMap[n.id];
|
||||||
|
return p.offset >= nLims.min && p.offset <= nLims.max;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Если нашли соседа - возвращаем его высоту, иначе 0 (не должно случаться при корректном Solver)
|
||||||
|
return neighbor ? neighbor.height : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Вычисляем высоту скругления для начала и конца стенки
|
||||||
|
// Высота не может быть больше самой стенки (p.height) и больше соседа
|
||||||
|
const startNeighborH = getNeighborHeight(pMin);
|
||||||
|
const endNeighborH = getNeighborHeight(pMax);
|
||||||
|
|
||||||
|
const hStart = Math.min(p.height, startNeighborH);
|
||||||
|
const hEnd = Math.min(p.height, endNeighborH);
|
||||||
|
|
||||||
|
// Функция добавления с конкретной высотой
|
||||||
|
const addFillet = (x: number, y: number, rotY: number, h: number) => {
|
||||||
|
if (h <= 1) return; // Если высота слишком мала, не рисуем
|
||||||
|
const geo = new THREE.ExtrudeGeometry(filletShape, { depth: h, bevelEnabled: false });
|
||||||
geo.rotateX(-Math.PI / 2);
|
geo.rotateX(-Math.PI / 2);
|
||||||
geo.rotateY(rotY);
|
geo.rotateY(rotY);
|
||||||
geo.translate(x, thickness, y);
|
geo.translate(x, thickness, y);
|
||||||
geometries.push(geo);
|
geometries.push(geo);
|
||||||
};
|
};
|
||||||
|
|
||||||
const h = thickness / 2;
|
const t = thickness / 2;
|
||||||
|
|
||||||
if (p.axis === 'x') {
|
if (p.axis === 'x') {
|
||||||
|
// VERTICAL WALL
|
||||||
const topY = (-innerDepth / 2) + (innerDepth * pMin);
|
const topY = (-innerDepth / 2) + (innerDepth * pMin);
|
||||||
addFillet(pX - h, topY, Math.PI);
|
|
||||||
addFillet(pX + h, topY, -Math.PI / 2);
|
|
||||||
|
|
||||||
const botY = (-innerDepth / 2) + (innerDepth * pMax);
|
const botY = (-innerDepth / 2) + (innerDepth * pMax);
|
||||||
addFillet(pX - h, botY, Math.PI / 2);
|
|
||||||
addFillet(pX + h, botY, 0);
|
|
||||||
} else {
|
|
||||||
const leftX = (-innerWidth / 2) + (innerWidth * pMin);
|
|
||||||
addFillet(leftX, pY - h, 0);
|
|
||||||
addFillet(leftX, pY + h, -Math.PI / 2);
|
|
||||||
|
|
||||||
|
// Top End (pMin) -> hStart
|
||||||
|
addFillet(pX - t, topY, Math.PI, hStart);
|
||||||
|
addFillet(pX + t, topY, -Math.PI / 2, hStart);
|
||||||
|
|
||||||
|
// Bottom End (pMax) -> hEnd
|
||||||
|
addFillet(pX - t, botY, Math.PI / 2, hEnd);
|
||||||
|
addFillet(pX + t, botY, 0, hEnd);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// HORIZONTAL WALL
|
||||||
|
const leftX = (-innerWidth / 2) + (innerWidth * pMin);
|
||||||
const rightX = (-innerWidth / 2) + (innerWidth * pMax);
|
const rightX = (-innerWidth / 2) + (innerWidth * pMax);
|
||||||
addFillet(rightX, pY - h, Math.PI / 2);
|
|
||||||
addFillet(rightX, pY + h, Math.PI);
|
// Left End (pMin) -> hStart
|
||||||
|
addFillet(leftX, pY - t, 0, hStart);
|
||||||
|
addFillet(leftX, pY + t, -Math.PI / 2, hStart);
|
||||||
|
|
||||||
|
// Right End (pMax) -> hEnd
|
||||||
|
addFillet(rightX, pY - t, Math.PI / 2, hEnd);
|
||||||
|
addFillet(rightX, pY + t, Math.PI, hEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user