This commit is contained in:
Халимов Рустам
2026-01-11 13:42:33 +03:00
parent d85cac8a16
commit 438c7b6615

View File

@@ -67,13 +67,12 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
let maxLimit = 1;
parts.forEach(p => {
// Если стенка параллельна нашей, мы ищем, не является ли она барьером
// Если стенка параллельна нашей
if (p.axis === axis) {
const pMin = p.min ?? 0;
const pMax = p.max ?? 1;
// Проверяем, пересекаются ли они "в проекции"
// crossPos - это середина нашей стенки. Попадает ли она в диапазон соседки?
// Проверяем пересечение проекций
if (crossPos > pMin && crossPos < pMax) {
if (p.offset < currentOffset) minLimit = Math.max(minLimit, p.offset);
if (p.offset > currentOffset) maxLimit = Math.min(maxLimit, p.offset);
@@ -83,7 +82,7 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
return { min: minLimit, max: maxLimit };
};
// Поиск границ для НОВОЙ стенки (чтобы обрезать её до T-соединения)
// Поиск границ для НОВОЙ стенки (T-соединения)
const getHoveredBoundaries = (lx: number, ly: number, parts: Partition[]) => {
let minX = 0, maxX = 1;
let minY = 0, maxY = 1;
@@ -93,13 +92,13 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
const pMax = p.max ?? 1;
if (p.axis === 'x') {
// Вертикальная стенка. Блокирует горизонтальное движение?
// Вертикальная стенка. Ограничивает по X.
if (ly >= pMin && ly <= pMax) {
if (p.offset < lx) minX = Math.max(minX, p.offset);
if (p.offset > lx) maxX = Math.min(maxX, p.offset);
}
} else {
// Горизонтальная стенка
// Горизонтальная стенка. Ограничивает по Y.
if (lx >= pMin && lx <= pMax) {
if (p.offset < ly) minY = Math.max(minY, p.offset);
if (p.offset > ly) maxY = Math.min(maxY, p.offset);
@@ -164,6 +163,7 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
newSplits[dragging.axis][dragging.index] = val;
onChange(newSplits);
} else {
// Dragging Partition
const [iStr, jStr] = dragging.cellKey.split('-');
const i = parseInt(iStr); const j = parseInt(jStr);
const cellX1 = sortedX[i]; const cellX2 = sortedX[i+1];
@@ -175,7 +175,11 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
} else {
newOffset = (ny - cellY1) / (cellY2 - cellY1);
}
// Ограничиваем перетаскивание.
// В идеале нужно динамически считать соседей, но для скорости пока ограничим ячейкой (2%-98%)
newOffset = Math.max(0.02, Math.min(0.98, newOffset));
if (!isNaN(newOffset)) {
updatePartition(dragging.cellKey, dragging.id, { offset: newOffset });
}
@@ -240,18 +244,23 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
if (found) {
setHoveredPartition({ id: found.id, cellKey: key });
} else {
// Calculate Phantom Partition
// Calculate Phantom Partition with T-junctions
const bounds = getHoveredBoundaries(lx, ly, parts);
const distL = lx - bounds.minX; const distR = bounds.maxX - lx;
const distT = ly - bounds.minY; const distB = bounds.maxY - ly;
const distL = lx - bounds.minX;
const distR = bounds.maxX - lx;
const distT = ly - bounds.minY;
const distB = bounds.maxY - ly;
const minX = Math.min(distL, distR);
const minY = Math.min(distT, distB);
const axis = minX < minY ? 'y' : 'x';
const width = bounds.maxX - bounds.minX;
const height = bounds.maxY - bounds.minY;
// Рисуем фантом только если есть место (>10% ячейки)
if ((axis === 'y' && height > 0.1) || (axis === 'x' && width > 0.1)) {
const offset = axis === 'x' ? lx : ly;
const min = axis === 'x' ? bounds.minY : bounds.minX;
@@ -308,7 +317,6 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
const x1 = sortedX[i]; const x2 = sortedX[i + 1];
const y1 = sortedY[j]; const y2 = sortedY[j + 1];
// !!! FIX: Проверка y2
if (y2 === undefined || x2 === undefined) continue;
const cellX = x1 * viewBoxW; const cellY = y1 * viewBoxH;
@@ -322,15 +330,16 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
const realW = (x2 - x1) * drawerW;
const realD = (y2 - y1) * drawerD;
// Label if empty
// --- LABEL FOR EMPTY CELL ---
if (parts.length === 0) {
const labelX = cellX + cellW / 2;
const labelY = cellY + cellH / 2;
const textW = Math.max(0, realW - wallThick).toFixed(0);
const textD = Math.max(0, realD - wallThick).toFixed(0);
// Увеличен шрифт до 16px
if (cellH > 40 && cellW > 60) {
elements.push(
<text key={`label-${key}`} x={labelX} y={labelY} textAnchor="middle" dominantBaseline="middle" className="fill-slate-300 font-mono text-[14px] font-bold pointer-events-none select-none opacity-80" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.8)' }}>
<text key={`label-${key}`} x={labelX} y={labelY} textAnchor="middle" dominantBaseline="middle" className="fill-slate-300 font-mono text-[16px] font-bold pointer-events-none select-none opacity-80" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.8)' }}>
{textW} × {textD}
</text>
);
@@ -344,36 +353,34 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
{parts.map(p => {
const pMin = p.min ?? 0; const pMax = p.max ?? 1;
let lx1, ly1, lx2, ly2;
let tx, ty;
let dist1 = 0, dist2 = 0;
// Ищем границы для размеров
let lx1, ly1, lx2, ly2; // Координаты линии
let dist1 = 0, dist2 = 0; // Расстояния
// Ищем соседей для расчета размеров
const neighbors = getNeighborOffsets(p.offset, (pMin + pMax)/2, p.axis, parts);
const isVertical = p.axis === 'x';
if (p.axis === 'x') {
if (isVertical) {
const px = cellX + (cellW * p.offset);
lx1 = px; lx2 = px;
ly1 = cellY + (cellH * pMin); ly2 = cellY + (cellH * pMax);
// Дистанция = (Мой Оффсет - Оффсет Соседа) * Ширину Ячейки - Толщина стенки
// Расчет расстояний по горизонтали
dist1 = Math.abs((p.offset - neighbors.min) * realW) - wallThick;
dist2 = Math.abs((neighbors.max - p.offset) * realW) - wallThick;
tx = px; ty = (ly1 + ly2) / 2;
} else {
const py = cellY + (cellH * p.offset);
ly1 = py; ly2 = py;
lx1 = cellX + (cellW * pMin); lx2 = cellX + (cellW * pMax);
// Расчет расстояний по вертикали
dist1 = Math.abs((p.offset - neighbors.min) * realD) - wallThick;
dist2 = Math.abs((neighbors.max - p.offset) * realD) - wallThick;
tx = (lx1 + lx2) / 2; ty = py;
}
const isSel = selectedPartitionId === p.id;
const isHov = hoveredPartition?.id === p.id;
const midX = (lx1 + lx2) / 2;
const midY = (ly1 + ly2) / 2;
const textOffset = 8; // Отступ текста от линии
return (
<g key={p.id}>
@@ -382,24 +389,31 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
<line x1={lx1} y1={ly1} x2={lx2} y2={ly2} stroke={isSel ? "#a855f7" : (isHov ? "#d8b4fe" : "#7e22ce")} strokeWidth={isSel ? 6 : 4} strokeLinecap="round" />
</g>
{/* DIMENSIONS */}
<text x={tx} y={ty} className="pointer-events-none select-none font-mono text-[12px] font-bold fill-white" textAnchor="middle" dominantBaseline="middle" style={{ textShadow: '0px 0px 3px #000' }}>
{p.axis === 'x' ? (
{/* DIMENSIONS - Увеличен шрифт до 14px */}
<g className="pointer-events-none select-none font-mono text-[14px] font-bold fill-white" style={{ textShadow: '0px 0px 3px #000' }}>
{isVertical ? (
// Для вертикальных линий: два отдельных текста слева и справа, выровненных по центру высоты
<>
<tspan dx="-18" fill="#e2e8f0">{Math.max(0, dist1).toFixed(0)}</tspan>
<tspan dx="36" fill="#e2e8f0">{Math.max(0, dist2).toFixed(0)}</tspan>
<text x={lx1 - textOffset} y={midY} textAnchor="end" dominantBaseline="middle">
{Math.max(0, dist1).toFixed(0)}
</text>
<text x={lx1 + textOffset} y={midY} textAnchor="start" dominantBaseline="middle">
{Math.max(0, dist2).toFixed(0)}
</text>
</>
) : (
<>
<tspan x={tx} dy="-12" fill="#e2e8f0">{Math.max(0, dist1).toFixed(0)}</tspan>
<tspan x={tx} dy="24" fill="#e2e8f0">{Math.max(0, dist2).toFixed(0)}</tspan>
</>
// Для горизонтальных линий: один текст по центру с tspan сверху и снизу
<text x={midX} y={midY} textAnchor="middle" dominantBaseline="middle">
<tspan x={midX} dy="-12">{Math.max(0, dist1).toFixed(0)}</tspan>
<tspan x={midX} dy="24">{Math.max(0, dist2).toFixed(0)}</tspan>
</text>
)}
</text>
</g>
</g>
);
})}
{/* Phantom Line */}
{isHovered && phantomPartition && !hoveredPartition && !dragging && (
<g className="pointer-events-none opacity-60">
{(() => {