From 16b92ca364a8c30e8ed89ce9086f35afee3f8056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B0=D0=BB=D0=B8=D0=BC=D0=BE=D0=B2=20=D0=A0=D1=83?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BC?= Date: Sat, 27 Dec 2025 21:04:08 +0300 Subject: [PATCH] Fix create --- src/services/geometryGenerator.ts | 76 +++++++++++++++---------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/src/services/geometryGenerator.ts b/src/services/geometryGenerator.ts index b83a9db..7dd5e51 100644 --- a/src/services/geometryGenerator.ts +++ b/src/services/geometryGenerator.ts @@ -11,6 +11,7 @@ export const calculateParts = ( ): 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]; @@ -24,12 +25,13 @@ export const calculateParts = ( const segmentW = (xPoints[i + 1] - xPoints[i]) * config.drawer.width; const segmentD = (yPoints[j + 1] - yPoints[j]) * config.drawer.depth; - // Apply Printer Tolerance + // Применяем зазор (Tolerance) const realWidth = segmentW - config.printerTolerance; const realDepth = segmentD - config.printerTolerance; const realX = segmentX + (config.printerTolerance / 2); const realY = segmentY + (config.printerTolerance / 2); + // Игнорируем слишком мелкие детали if (realWidth < 5 || realDepth < 5) { continue; } @@ -52,25 +54,25 @@ export const calculateParts = ( }; /** - * Helper to create a rounded rectangle Shape + * Создает 2D форму прямоугольника со скругленными краями */ const createRoundedRectShape = (width: number, height: number, radius: number): THREE.Shape => { const shape = new THREE.Shape(); const x = -width / 2; const y = -height / 2; - // Clamp radius to not exceed half of width or height + // Ограничиваем радиус, чтобы он не сломал геометрию (не больше половины стороны) const r = Math.min(radius, width / 2, height / 2); - if (r <= 0) { - // Regular rectangle + 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 { - // Rounded rectangle + // Прямоугольник со скруглениями shape.moveTo(x, y + r); shape.lineTo(x, y + height - r); shape.quadraticCurveTo(x, y + height, x + r, y + height); @@ -86,42 +88,34 @@ const createRoundedRectShape = (width: number, height: number, radius: number): } /** - * Generates a Three.js Geometry for a hollow bin with fillets (rounded corners). + * Генерирует 3D геометрию ящика */ export const createBinGeometry = ( width: number, depth: number, height: number, thickness: number, - radius: number = 0 // Default radius + radius: number = 0 ): THREE.BufferGeometry => { - // 1. Создаем форму ДНА (Floor) - // Это сплошной прямоугольник со скруглениями + // 1. ГЕОМЕТРИЯ ДНА (Сплошная) const floorShape = createRoundedRectShape(width, depth, radius); const floorGeo = new THREE.ExtrudeGeometry(floorShape, { - depth: thickness, // Толщина дна + depth: thickness, // Выдавливаем на толщину дна bevelEnabled: false, - curveSegments: 12 // Гладкость скругления + curveSegments: 16 // Количество сегментов на скруглениях }); - // ExtrudeGeometry создает объект "лежа" на XY, нам нужно повернуть его, чтобы он стал дном (XZ) - floorGeo.rotateX(Math.PI / 2); - // Сдвигаем на высоту thickness, так как extrude идет в +Z (после поворота это +Y, но перевернуто... проще подобрать) - // По умолчанию extrude создает от 0 до Z. После поворота X(90deg): - // Z становится -Y. То есть дно уходит вниз от 0. - // Нам нужно, чтобы дно было от 0 до thickness по Y. - // Возвращаем поворот в -90 (стандарт для топологии) - floorGeo.rotateX(-Math.PI); - floorGeo.translate(0, thickness, 0); // Поднимаем, чтобы верх дна был на уровне thickness + // Extrude выдавливает по оси Z. Нам нужно повернуть, чтобы "глубина" стала "высотой" (Y). + // Поворот на -90 градусов вокруг X кладет Z на Y. + floorGeo.rotateX(-Math.PI / 2); + // Теперь дно занимает пространство от Y=0 до Y=thickness. - - // 2. Создаем форму СТЕНОК (Walls) - // Это внешняя форма МИНУС внутренняя форма (дырка) + // 2. ГЕОМЕТРИЯ СТЕНОК (С дыркой) const outerShape = createRoundedRectShape(width, depth, radius); - // Внутренний радиус должен быть меньше внешнего на толщину стенки, но не меньше 0 + // Вырезаем внутреннюю часть const innerRadius = Math.max(0, radius - thickness); const innerWidth = width - (2 * thickness); const innerDepth = depth - (2 * thickness); @@ -131,29 +125,33 @@ export const createBinGeometry = ( outerShape.holes.push(innerHole); } + // Высота стенок = общая высота минус толщина дна const wallHeight = height - thickness; const wallGeo = new THREE.ExtrudeGeometry(outerShape, { depth: wallHeight, bevelEnabled: false, - curveSegments: 12 + curveSegments: 16 }); - - // Поворачиваем стенки так же, как дно - wallGeo.rotateX(Math.PI / 2); - wallGeo.rotateX(-Math.PI); - // Стенки начинаются ОТ дна (y = thickness) - wallGeo.translate(0, thickness + wallHeight, 0); - // Объединяем геометрии + // Поворачиваем стенки так же, как дно + wallGeo.rotateX(-Math.PI / 2); + + // Сейчас стенки тоже начинаются с Y=0. + // Нам нужно поднять их НАД дном. + wallGeo.translate(0, thickness, 0); + + // Теперь стенки занимают пространство от Y=thickness до Y=height. + + // 3. ОБЪЕДИНЕНИЕ + // Сливаем две геометрии в одну. Слайсеры поймут это как единый объект, + // так как поверхности идеально соприкасаются. const merged = mergeBufferGeometries([floorGeo, wallGeo]); - // Центрируем геометрию, чтобы Pivot был в центре дна (как раньше в BinMesh) - // Ранее мы использовали BoxGeometry который центрирован. - // Extrude создает форму вокруг (0,0), так что по X/Z она уже центрирована. - // По Y она сейчас от 0 до height. - // Вернем как было: Pivot внизу. - + // Центрирование не нужно, так как createRoundedRectShape строит форму вокруг (0,0) по X и Z. + // А по Y мы выстроили от 0 вверх. + // Pivot point (опорная точка) осталась внизу в центре (0,0,0), что идеально для позиционирования. + return merged || new THREE.BoxGeometry(1, 1, 1); };