This commit is contained in:
Халимов Рустам
2026-01-10 23:42:23 +03:00
parent b422cdef6b
commit 4b4ebb68d1

View File

@@ -46,6 +46,9 @@ export const calculateParts = (config: AppConfig, splits: LayoutSplits): Generat
return parts;
};
// --- ГЕОМЕТРИЯ ---
// 1. Форма скругленного прямоугольника (для дна и внешних стенок)
const createRoundedRectShape = (width: number, height: number, radius: number): THREE.Shape => {
const shape = new THREE.Shape();
const x = -width / 2;
@@ -72,16 +75,35 @@ const createRoundedRectShape = (width: number, height: number, radius: number):
return shape;
};
// 2. Форма галтели (вогнутого треугольника) для углов
const createFilletShape = (radius: number): THREE.Shape => {
const shape = new THREE.Shape();
// Начинаем из угла (0,0)
shape.moveTo(0, 0);
// Линия вдоль одной стенки
shape.lineTo(radius, 0);
// Вогнутая дуга к другой стенке
// Центр окружности (radius, radius), радиус radius.
// Рисуем дугу от 270 (-PI/2) до 180 (PI) градусов по часовой стрелке
shape.absarc(radius, radius, radius, 1.5 * Math.PI, Math.PI, true);
// Замыкаем в угол
shape.lineTo(0, 0);
return shape;
};
export const createBinGeometry = (
width: number, depth: number, height: number, thickness: number, radius: number = 0, partitions: Partition[] = []
): THREE.BufferGeometry => {
const geometries: THREE.BufferGeometry[] = [];
// ДНО
const floorShape = createRoundedRectShape(width, depth, radius);
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);
const innerWidth = width - (2 * thickness);
@@ -98,6 +120,7 @@ export const createBinGeometry = (
wallGeo.translate(0, thickness, 0);
geometries.push(wallGeo);
// ВНУТРЕННИЕ ПЕРЕГОРОДКИ И СКРУГЛЕНИЯ
partitions.forEach(p => {
const pMin = p.min ?? 0;
const pMax = p.max ?? 1;
@@ -106,6 +129,7 @@ export const createBinGeometry = (
let pWidth = 0, pDepth = 0, pX = 0, pY = 0;
// Размеры самой стенки
if (p.axis === 'x') {
pWidth = thickness;
pDepth = lengthRatio * innerDepth;
@@ -118,12 +142,73 @@ export const createBinGeometry = (
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 });
// Создаем стенку
const partShape = createRoundedRectShape(pWidth, pDepth, 0.1); // Чуть-чуть скругляем саму стенку, чтобы не была острой
const partGeo = new THREE.ExtrudeGeometry(partShape, { depth: p.height, bevelEnabled: false, curveSegments: 2 });
partGeo.rotateX(-Math.PI / 2);
partGeo.translate(pX, thickness, pY);
geometries.push(partGeo);
// --- ДОБАВЛЕНИЕ ГАЛТЕЛЕЙ (FILLETS) ---
if (p.rounded && radius > 0.5) {
// Радиус скругления такой же, как у углов ящика, но не больше разумного предела
const filletR = Math.min(radius, 6);
const filletShape = createFilletShape(filletR);
const filletExtrudeSettings = { depth: p.height, bevelEnabled: false, curveSegments: 8 };
// Функция для создания и позиционирования одной галтели
const addFillet = (x: number, y: number, rotation: number) => {
const geo = new THREE.ExtrudeGeometry(filletShape, filletExtrudeSettings);
geo.rotateX(-Math.PI / 2); // Положить на пол
geo.rotateY(rotation); // Повернуть в нужный угол
// Корректировка позиции после вращения вокруг (0,0)
// Нам нужно сместить так, чтобы угол (0,0) галтели совпал с углом стыка
geo.translate(x, thickness, y);
geometries.push(geo);
};
const halfThick = thickness / 2;
if (p.axis === 'x') {
// Вертикальная стенка. Концы: Top (pMin) и Bottom (pMax) (в 2D координатах Y)
// Y координата начала: -innerDepth/2 + innerDepth * pMin
// Y координата конца: -innerDepth/2 + innerDepth * pMax
// X координата центра: pX
const startY = (-innerDepth / 2) + (innerDepth * pMin);
const endY = (-innerDepth / 2) + (innerDepth * pMax);
// 4 Угла:
// 1. Start Left: X = pX - halfThick, Y = startY. Rotation: 0 (смотрит вправо-вверх? нет)
// Галтель рисуется в +X, +Y квадранте от 0,0.
// Нам нужно заполнить угол между стенкой (идет вниз) и перпендикуляром.
// Start (Top in 2D view, actually Min Y in 3D logic here usually means "Back")
// Let's assume standard plan view:
// Min Y is "Top" edge visually in SVG usually 0. In 3D Z is Y.
// Min End (Start of wall):
addFillet(pX - halfThick, startY, 0); // Left side, pointing towards +Z (down in visual)
addFillet(pX + halfThick, startY, -Math.PI/2); // Right side
// Max End (End of wall):
addFillet(pX - halfThick, endY, Math.PI/2); // Left side
addFillet(pX + halfThick, endY, Math.PI); // Right side
} else {
// Горизонтальная стенка
const startX = (-innerWidth / 2) + (innerWidth * pMin);
const endX = (-innerWidth / 2) + (innerWidth * pMax);
// Min End (Left side):
addFillet(startX, pY + halfThick, -Math.PI/2);
addFillet(startX, pY - halfThick, 0);
// Max End (Right side):
addFillet(endX, pY + halfThick, Math.PI);
addFillet(endX, pY - halfThick, Math.PI/2);
}
}
});
const merged = mergeBufferGeometries(geometries);