2
This commit is contained in:
@@ -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">
|
||||
{(() => {
|
||||
|
||||
Reference in New Issue
Block a user