diff --git a/src/App.tsx b/src/App.tsx index b1990e4..e65121c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,11 +19,9 @@ const App = () => { cornerRadius: 4, }); - // ИСПРАВЛЕНИЕ: Добавлено поле subdivisions const [splits, setSplits] = useState({ x: [], - y: [], - subdivisions: {} + y: [] }); // --- ЛОГИКА ВОССТАНОВЛЕНИЯ ИЗ ССЫЛКИ --- @@ -31,11 +29,7 @@ const App = () => { const sharedData = parseShareUrl(); if (sharedData) { setConfig(sharedData.config); - // Если в старой ссылке нет subdivisions, добавляем пустое - setSplits({ - ...sharedData.splits, - subdivisions: sharedData.splits.subdivisions || {} - }); + setSplits(sharedData.splits); setStep(3); setIsLoadedFromUrl(true); window.history.replaceState({}, '', window.location.pathname); @@ -128,7 +122,7 @@ const App = () => { - - - -
+
- Режим: {mode === 'lines' ? 'Границы' : 'Ячейки'} + Инструкция
- {mode === 'lines' ? ( -
    -
  • Клик: Новая линия
  • -
  • Драг: Двигать линию
  • -
  • ПКМ: Удалить линию
  • -
- ) : ( -
    -
  • Клик по ячейке: Выбрать
  • -
  • Используй меню для деления внутри
  • -
- )} +
    +
  • Клик у края: Новая линия
  • +
  • Перетаскивание: Изменить размер
  • +
  • Двойной клик/ПКМ: Удалить
  • +
@@ -191,12 +134,12 @@ export const LayoutStep: React.FC = ({ config, splits, onChange }) => {
@@ -216,128 +159,111 @@ export const LayoutStep: React.FC = ({ config, splits, onChange }) => { + {/* --- Labels --- */} {sortedX.slice(0, -1).map((x1, i) => { const x2 = sortedX[i + 1]; return sortedY.slice(0, -1).map((y1, j) => { - const cellX = x1 * viewBoxW; - const cellY = y1 * viewBoxH; - const cellW = (x2 - x1) * viewBoxW; - const cellH = (y2 - y1) * viewBoxH; + const y2 = sortedY[j + 1]; + const width = (x2 - x1) * config.drawer.width; + const depth = (y2 - y1) * config.drawer.depth; + const centerX = ((x1 + x2) / 2) * viewBoxW; + const centerY = ((y1 + y2) / 2) * viewBoxH; - const isSelected = selectedCell?.i === i && selectedCell?.j === j; - const subdiv = getSubdivision(i, j); + const cellWidthSVG = (x2 - x1) * viewBoxW; + const cellHeightSVG = (y2 - y1) * viewBoxH; + + let fontSize = Math.min(36, cellHeightSVG * 0.6); + fontSize = Math.min(fontSize, cellWidthSVG * 0.25); + + if (fontSize < 10) return null; return ( - - { - if (mode === 'cells') { - e.stopPropagation(); - setSelectedCell({ i, j }); - } - }} - /> - {subdiv.cols > 1 && Array.from({ length: subdiv.cols - 1 }).map((_, cI) => { - const splitX = cellX + (cellW / subdiv.cols) * (cI + 1); - return ; - })} - {subdiv.rows > 1 && Array.from({ length: subdiv.rows - 1 }).map((_, rI) => { - const splitY = cellY + (cellH / subdiv.rows) * (rI + 1); - return ; - })} - {subdiv.rows === 1 && subdiv.cols === 1 && ( - - {((x2 - x1) * config.drawer.width).toFixed(0)}×{((y2 - y1) * config.drawer.depth).toFixed(0)} - - )} - + + {width.toFixed(0)} × {depth.toFixed(0)} + ); }); })} + + {/* --- X Lines (Vertical) --- */} + {splits.x.map((x, i) => { + const isHovered = hoveredSplit?.axis === 'x' && hoveredSplit.index === i; + const isDragging = dragging?.axis === 'x' && dragging.index === i; + const color = isHovered || isDragging ? '#f59e0b' : '#64748b'; + const width = isHovered || isDragging ? 8 : 4; - {safeX.map((x, i) => ( - handleSplitHover(e, 'x', i)}> - - - {mode === 'lines' && hoveredSplit?.axis === 'x' && hoveredSplit.index === i && ( - { e.stopPropagation(); removeSplit('x', i); }} - onMouseEnter={() => setIsButtonHovered(true)} onMouseLeave={() => setIsButtonHovered(false)} - > - - - - )} - - ))} + return ( + removeSplit('x', i)} + onMouseMove={(e) => handleSplitHover(e, 'x', i)} + > + + + {(isHovered || isDragging) && ( + { e.stopPropagation(); removeSplit('x', i); }} + onMouseEnter={() => setIsButtonHovered(true)} onMouseLeave={() => setIsButtonHovered(false)} + > + + + + )} + + ); + })} - {safeY.map((y, i) => ( - handleSplitHover(e, 'y', i)}> - - - {mode === 'lines' && hoveredSplit?.axis === 'y' && hoveredSplit.index === i && ( - { e.stopPropagation(); removeSplit('y', i); }} - onMouseEnter={() => setIsButtonHovered(true)} onMouseLeave={() => setIsButtonHovered(false)} - > - - - - )} - - ))} + {/* --- Y Lines (Horizontal) --- */} + {splits.y.map((y, i) => { + const isHovered = hoveredSplit?.axis === 'y' && hoveredSplit.index === i; + const isDragging = dragging?.axis === 'y' && dragging.index === i; + const color = isHovered || isDragging ? '#f59e0b' : '#64748b'; + const width = isHovered || isDragging ? 8 : 4; - {mode === 'lines' && !hoveredSplit && !dragging && phantomAxis === 'x' && ( - + return ( + removeSplit('y', i)} + onMouseMove={(e) => handleSplitHover(e, 'y', i)} + > + + + {(isHovered || isDragging) && ( + { e.stopPropagation(); removeSplit('y', i); }} + onMouseEnter={() => setIsButtonHovered(true)} onMouseLeave={() => setIsButtonHovered(false)} + > + + + + )} + + ); + })} + + {/* --- Phantom Lines --- */} + {!hoveredSplit && !dragging && !isButtonHovered && phantomAxis === 'x' && ( + + + + )} - {mode === 'lines' && !hoveredSplit && !dragging && phantomAxis === 'y' && ( - + {!hoveredSplit && !dragging && !isButtonHovered && phantomAxis === 'y' && ( + + + + )} - - {mode === 'cells' && selectedCell && ( -
e.stopPropagation()} - > -
- - - {getSubdivision(selectedCell.i, selectedCell.j).cols} - -
- -
- - - {getSubdivision(selectedCell.i, selectedCell.j).rows} - -
- - -
- )} -
diff --git a/src/services/geometryGenerator.ts b/src/services/geometryGenerator.ts index a792c78..af173b0 100644 --- a/src/services/geometryGenerator.ts +++ b/src/services/geometryGenerator.ts @@ -16,57 +16,41 @@ export const calculateParts = ( for (let i = 0; i < xPoints.length - 1; i++) { for (let j = 0; j < yPoints.length - 1; j++) { - const rawX = xPoints[i] * config.drawer.width; - const rawY = yPoints[j] * config.drawer.depth; - const rawW = (xPoints[i + 1] - xPoints[i]) * config.drawer.width; - const rawD = (yPoints[j + 1] - yPoints[j]) * config.drawer.depth; + const segmentX = xPoints[i] * config.drawer.width; + const segmentY = yPoints[j] * config.drawer.depth; + const segmentW = (xPoints[i + 1] - xPoints[i]) * config.drawer.width; + const segmentD = (yPoints[j + 1] - yPoints[j]) * config.drawer.depth; - // Получаем настройки деления для этой ячейки - const subdiv = splits.subdivisions?.[`${i}-${j}`] || { rows: 1, cols: 1 }; - - const subCellWidth = rawW / subdiv.cols; - const subCellDepth = rawD / subdiv.rows; + const realWidth = segmentW - config.printerTolerance; + const realDepth = segmentD - config.printerTolerance; + const realX = segmentX + (config.printerTolerance / 2); + const realY = segmentY + (config.printerTolerance / 2); - // Генерируем под-ячейки - for (let r = 0; r < subdiv.rows; r++) { - for (let c = 0; c < subdiv.cols; c++) { - - const subX = rawX + (c * subCellWidth); - const subY = rawY + (r * subCellDepth); - - const realWidth = subCellWidth - config.printerTolerance; - const realDepth = subCellDepth - config.printerTolerance; - const realX = subX + (config.printerTolerance / 2); - const realY = subY + (config.printerTolerance / 2); - - if (realWidth < 5 || realDepth < 5) continue; - - // Формируем имя: если ячейка поделена, добавляем индексы (1-1, 1-2...) - let partName = `Ячейка ${i+1}-${j+1}`; - if (subdiv.rows > 1 || subdiv.cols > 1) { - partName += ` (${r+1}-${c+1})`; - } - - parts.push({ - id: `part-${partCounter}`, - name: partName, - width: realWidth, - depth: realDepth, - height: config.drawer.height, - x: realX, - y: realY, - color: `hsl(${Math.random() * 360}, 70%, 50%)` - }); - partCounter++; - } + if (realWidth < 5 || realDepth < 5) { + continue; } + + parts.push({ + id: `part-${partCounter}`, + name: `Ячейка ${i+1}-${j+1}`, + width: realWidth, + depth: realDepth, + height: config.drawer.height, + x: realX, + y: realY, + color: `hsl(${Math.random() * 360}, 70%, 50%)` + }); + partCounter++; } } return parts; }; -// ... Вспомогательные функции генерации геометрии (без изменений) ... +// ... Вспомогательные функции (createRoundedRectShape, createBinGeometry) остаются ТЕМИ ЖЕ, +// что и в прошлой работающей версии (со скруглениями и Extrude). +// Они не менялись при внедрении subdivisions, но для целостности я их продублирую. + const createRoundedRectShape = (width: number, height: number, radius: number): THREE.Shape => { const shape = new THREE.Shape(); const x = -width / 2; @@ -100,9 +84,12 @@ export const createBinGeometry = ( thickness: number, radius: number = 0 ): THREE.BufferGeometry => { + const floorShape = createRoundedRectShape(width, depth, radius); const floorGeo = new THREE.ExtrudeGeometry(floorShape, { - depth: thickness, bevelEnabled: false, curveSegments: 16 + depth: thickness, + bevelEnabled: false, + curveSegments: 16 }); floorGeo.rotateX(-Math.PI / 2); @@ -118,7 +105,9 @@ export const createBinGeometry = ( const wallHeight = height - thickness; const wallGeo = new THREE.ExtrudeGeometry(outerShape, { - depth: wallHeight, bevelEnabled: false, curveSegments: 16 + depth: wallHeight, + bevelEnabled: false, + curveSegments: 16 }); wallGeo.rotateX(-Math.PI / 2); diff --git a/src/types.ts b/src/types.ts index f7c9591..1bdb5ff 100644 --- a/src/types.ts +++ b/src/types.ts @@ -11,16 +11,9 @@ export interface AppConfig { cornerRadius: number; } -export interface CellSubdivision { - rows: number; - cols: number; -} - export interface LayoutSplits { x: number[]; y: number[]; - // Добавляем обязательное поле, но разрешаем ему быть пустым - subdivisions: Record; } export interface GeneratedPart {