Add sizes
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React, { Suspense, useEffect, useRef, useState, useMemo } from 'react';
|
import React, { Suspense, useEffect, useRef, useState, useMemo } from 'react';
|
||||||
import { Canvas } from '@react-three/fiber';
|
import { Canvas } from '@react-three/fiber';
|
||||||
import { OrbitControls, Center, Environment } from '@react-three/drei';
|
import { OrbitControls, Center, Environment, Text } from '@react-three/drei';
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import JSZip from 'jszip';
|
import JSZip from 'jszip';
|
||||||
import { AppConfig, GeneratedPart, LayoutSplits, PerforationConfig } from '../types';
|
import { AppConfig, GeneratedPart, LayoutSplits, PerforationConfig } from '../types';
|
||||||
@@ -52,18 +52,138 @@ const BinMesh: React.FC<BinMeshProps> = ({ part, thickness, cornerRadius, perfor
|
|||||||
return new THREE.EdgesGeometry(geometry, 20);
|
return new THREE.EdgesGeometry(geometry, 20);
|
||||||
}, [geometry]);
|
}, [geometry]);
|
||||||
|
|
||||||
|
// 3. Вычисляем размеры и позиции для каждой под-ячейки
|
||||||
|
// 3. Вычисляем размеры и позиции для реальных отсеков (с учетом мерджинга)
|
||||||
|
const dimLabels = useMemo(() => {
|
||||||
|
const xOffsets = new Set<number>([0, 1]);
|
||||||
|
const zOffsets = new Set<number>([0, 1]);
|
||||||
|
|
||||||
|
part.internalPartitions.forEach(p => {
|
||||||
|
if (p.axis === 'x') xOffsets.add(p.offset);
|
||||||
|
if (p.axis === 'y') zOffsets.add(p.offset);
|
||||||
|
});
|
||||||
|
|
||||||
|
const xSplits = Array.from(xOffsets).sort((a, b) => a - b);
|
||||||
|
const zSplits = Array.from(zOffsets).sort((a, b) => a - b);
|
||||||
|
|
||||||
|
const numCols = xSplits.length - 1;
|
||||||
|
const numRows = zSplits.length - 1;
|
||||||
|
if (numCols === 0 || numRows === 0) return [];
|
||||||
|
|
||||||
|
// Union-Find для объединения ячеек, не разделенных стеной
|
||||||
|
const parent = new Int32Array(numCols * numRows).map((_, i) => i);
|
||||||
|
const find = (i: number): number => {
|
||||||
|
if (parent[i] === i) return i;
|
||||||
|
parent[i] = find(parent[i]);
|
||||||
|
return parent[i];
|
||||||
|
}
|
||||||
|
const union = (i: number, j: number) => {
|
||||||
|
const rootI = find(i);
|
||||||
|
const rootJ = find(j);
|
||||||
|
if (rootI !== rootJ) parent[rootJ] = rootI;
|
||||||
|
}
|
||||||
|
const getIdx = (c: number, r: number) => r * numCols + c;
|
||||||
|
|
||||||
|
// Вертикальные границы (X)
|
||||||
|
for (let i = 0; i < numCols - 1; i++) {
|
||||||
|
const boundaryX = xSplits[i + 1];
|
||||||
|
for (let j = 0; j < numRows; j++) {
|
||||||
|
const zMid = (zSplits[j] + zSplits[j + 1]) / 2;
|
||||||
|
// Проверяем наличие перегородки axis='x'
|
||||||
|
const isBlocked = part.internalPartitions.some(p =>
|
||||||
|
p.axis === 'x' &&
|
||||||
|
Math.abs(p.offset - boundaryX) < 0.001 &&
|
||||||
|
(p.min ?? 0) <= zMid && (p.max ?? 1) >= zMid
|
||||||
|
);
|
||||||
|
if (!isBlocked) union(getIdx(i, j), getIdx(i + 1, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Горизонтальные границы (Z/Y)
|
||||||
|
for (let j = 0; j < numRows - 1; j++) {
|
||||||
|
const boundaryZ = zSplits[j + 1];
|
||||||
|
for (let i = 0; i < numCols; i++) {
|
||||||
|
const xMid = (xSplits[i] + xSplits[i + 1]) / 2;
|
||||||
|
// Проверяем наличие перегородки axis='y'
|
||||||
|
const isBlocked = part.internalPartitions.some(p =>
|
||||||
|
p.axis === 'y' &&
|
||||||
|
Math.abs(p.offset - boundaryZ) < 0.001 &&
|
||||||
|
(p.min ?? 0) <= xMid && (p.max ?? 1) >= xMid
|
||||||
|
);
|
||||||
|
if (!isBlocked) union(getIdx(i, j), getIdx(i, j + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Агрегируем регионы
|
||||||
|
const regions: Record<number, { minC: number, maxC: number, minR: number, maxR: number }> = {};
|
||||||
|
for (let j = 0; j < numRows; j++) {
|
||||||
|
for (let i = 0; i < numCols; i++) {
|
||||||
|
const root = find(getIdx(i, j));
|
||||||
|
if (!regions[root]) regions[root] = { minC: i, maxC: i, minR: j, maxR: j };
|
||||||
|
else {
|
||||||
|
const r = regions[root];
|
||||||
|
r.minC = Math.min(r.minC, i);
|
||||||
|
r.maxC = Math.max(r.maxC, i);
|
||||||
|
r.minR = Math.min(r.minR, j);
|
||||||
|
r.maxR = Math.max(r.maxR, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.values(regions).map((r, idx) => {
|
||||||
|
const fXStart = xSplits[r.minC];
|
||||||
|
const fXEnd = xSplits[r.maxC + 1];
|
||||||
|
const fZStart = zSplits[r.minR];
|
||||||
|
const fZEnd = zSplits[r.maxR + 1];
|
||||||
|
|
||||||
|
const fracW = fXEnd - fXStart;
|
||||||
|
const fracD = fZEnd - fZStart;
|
||||||
|
|
||||||
|
const dimX = Math.max(0, fracW * (part.width - thickness) - thickness);
|
||||||
|
const dimZ = Math.max(0, fracD * (part.depth - thickness) - thickness);
|
||||||
|
|
||||||
|
const cx = -part.width / 2 + (fXStart + fXEnd) / 2 * part.width;
|
||||||
|
const cz = -part.depth / 2 + (fZStart + fZEnd) / 2 * part.depth;
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: `region-${idx}`,
|
||||||
|
pos: [cx, thickness + 0.2, cz] as [number, number, number],
|
||||||
|
text: `${dimX.toFixed(0)} x ${dimZ.toFixed(0)}`
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [part, thickness]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<group position={[part.x + part.width / 2, 0, part.y + part.depth / 2]}>
|
<group
|
||||||
|
position={[part.x + part.width / 2, 0, part.y + part.depth / 2]}
|
||||||
|
onClick={(e) => { e.stopPropagation(); onClick(); }}
|
||||||
|
>
|
||||||
{/* Сама модель */}
|
{/* Сама модель */}
|
||||||
<mesh geometry={geometry} onClick={(e) => { e.stopPropagation(); onClick(); }}>
|
<mesh geometry={geometry}>
|
||||||
<meshStandardMaterial
|
<meshStandardMaterial
|
||||||
color={isSelected ? '#f59e0b' : part.color}
|
color={isSelected ? '#f59e0b' : part.color}
|
||||||
roughness={0.5}
|
roughness={0.5}
|
||||||
metalness={0.1}
|
metalness={0.1}
|
||||||
side={THREE.DoubleSide} // Рисуем обе стороны стенок
|
side={THREE.DoubleSide}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|
||||||
|
{/* Текстовые метки размеров внутри каждой ячейки */}
|
||||||
|
{dimLabels.map(label => (
|
||||||
|
<Text
|
||||||
|
key={label.key}
|
||||||
|
position={label.pos}
|
||||||
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
|
fontSize={Math.min(part.width, part.depth) * 0.035}
|
||||||
|
color="#1e293b"
|
||||||
|
anchorX="center"
|
||||||
|
anchorY="middle"
|
||||||
|
characters="0123456789x "
|
||||||
|
>
|
||||||
|
{label.text}
|
||||||
|
</Text>
|
||||||
|
))}
|
||||||
|
|
||||||
{/* Белая подсветка при выборе */}
|
{/* Белая подсветка при выборе */}
|
||||||
{isSelected && (
|
{isSelected && (
|
||||||
<lineSegments geometry={edgesGeometry}>
|
<lineSegments geometry={edgesGeometry}>
|
||||||
|
|||||||
Reference in New Issue
Block a user