Font size for sizes
This commit is contained in:
@@ -61,37 +61,40 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
};
|
};
|
||||||
const selectedData = getSelectedPartition();
|
const selectedData = getSelectedPartition();
|
||||||
|
|
||||||
// Найти ближайшие границы для перегородки (или точки)
|
// --- ИСПРАВЛЕННЫЙ РАСЧЕТ СОСЕДЕЙ ДЛЯ РАЗМЕРОВ ---
|
||||||
const getNearestNeighbors = (lx: number, ly: number, axis: Axis, parts: Partition[]) => {
|
// targetOffset: позиция стенки (0..1) по её основной оси
|
||||||
|
// crossPos: позиция центра стенки по перпендикулярной оси (чтобы понять, пересекаются ли они)
|
||||||
|
// axis: ось самой стенки ('x' или 'y')
|
||||||
|
const getNearestNeighbors = (targetOffset: number, crossPos: number, axis: Axis, parts: Partition[]) => {
|
||||||
let min = 0;
|
let min = 0;
|
||||||
let max = 1;
|
let max = 1;
|
||||||
|
|
||||||
parts.forEach(p => {
|
parts.forEach(p => {
|
||||||
// Игнорируем саму себя, если это проверка для существующей стенки (но здесь мы передаем lx/ly точки)
|
|
||||||
|
|
||||||
const pMin = p.min ?? 0;
|
const pMin = p.min ?? 0;
|
||||||
const pMax = p.max ?? 1;
|
const pMax = p.max ?? 1;
|
||||||
|
|
||||||
// Если мы ищем границы по X (для вертикальной стенки или пространства слева/справа)
|
|
||||||
// То нас интересуют другие ВЕРТИКАЛЬНЫЕ стенки (axis === 'x'), которые перекрывают нас по Y
|
|
||||||
if (axis === 'x') {
|
if (axis === 'x') {
|
||||||
|
// Мы - вертикальная стенка (X). Ищем другие ВЕРТИКАЛЬНЫЕ стенки слева/справа.
|
||||||
if (p.axis === 'x') {
|
if (p.axis === 'x') {
|
||||||
// Перекрываются ли диапазоны высоты?
|
// Они должны перекрываться с нами по высоте (Y)
|
||||||
// ly для точки - это просто координата. Для стенки это min-max.
|
// Наша "высота" - это точка crossPos (середина).
|
||||||
// Здесь мы ищем границы для точки (lx, ly).
|
// Стенка P занимает по Y от pMin до pMax.
|
||||||
// Стенка P идет от pMin до pMax по Y.
|
if (crossPos > pMin && crossPos < pMax) {
|
||||||
if (ly > pMin && ly < pMax) {
|
// Стенка P находится слева от нас?
|
||||||
if (p.offset < lx) min = Math.max(min, p.offset);
|
if (p.offset < targetOffset) min = Math.max(min, p.offset);
|
||||||
if (p.offset > lx) max = Math.min(max, p.offset);
|
// Стенка P находится справа от нас?
|
||||||
|
if (p.offset > targetOffset) max = Math.min(max, p.offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ищем границы по Y (для горизонтальной стенки)
|
// Мы - горизонтальная стенка (Y). Ищем другие ГОРИЗОНТАЛЬНЫЕ стенки сверху/снизу.
|
||||||
// Нас интересуют ГОРИЗОНТАЛЬНЫЕ стенки
|
|
||||||
if (p.axis === 'y') {
|
if (p.axis === 'y') {
|
||||||
if (lx > pMin && lx < pMax) {
|
// Они должны перекрываться с нами по ширине (X)
|
||||||
if (p.offset < ly) min = Math.max(min, p.offset);
|
// Наша "ширина" - это точка crossPos (середина).
|
||||||
if (p.offset > ly) max = Math.min(max, p.offset);
|
// Стенка P занимает по X от pMin до pMax.
|
||||||
|
if (crossPos > pMin && crossPos < pMax) {
|
||||||
|
if (p.offset < targetOffset) min = Math.max(min, p.offset);
|
||||||
|
if (p.offset > targetOffset) max = Math.min(max, p.offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +102,7 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
return { min, max };
|
return { min, max };
|
||||||
};
|
};
|
||||||
|
|
||||||
// Расчет границ для НОВОЙ линии (перпендикулярной)
|
// Расчет границ для НОВОЙ линии (T-соединения)
|
||||||
const getHoveredBoundaries = (lx: number, ly: number, parts: Partition[]) => {
|
const getHoveredBoundaries = (lx: number, ly: number, parts: Partition[]) => {
|
||||||
let minX = 0, maxX = 1;
|
let minX = 0, maxX = 1;
|
||||||
let minY = 0, maxY = 1;
|
let minY = 0, maxY = 1;
|
||||||
@@ -181,9 +184,8 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
const cellY1 = sortedY[j]; const cellY2 = sortedY[j+1];
|
const cellY1 = sortedY[j]; const cellY2 = sortedY[j+1];
|
||||||
|
|
||||||
let newOffset = 0;
|
let newOffset = 0;
|
||||||
// Текущие границы перетаскиваемой стенки (чтобы не выехать за пределы соседей)
|
// Ограничиваем перетаскивание пределами "родительской" зоны
|
||||||
// Для простоты пока используем границы ячейки (0-1), так как сложный пересчет соседей в реалтайме - это дорого
|
// Для T-соединений это сложнее, но пока ограничим ячейкой (0-1)
|
||||||
// Но визуально мы ограничим 2-98%
|
|
||||||
if (dragging.axis === 'x') {
|
if (dragging.axis === 'x') {
|
||||||
newOffset = (nx - cellX1) / (cellX2 - cellX1);
|
newOffset = (nx - cellX1) / (cellX2 - cellX1);
|
||||||
} else {
|
} else {
|
||||||
@@ -330,24 +332,19 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
const parts = safePartitions[key] || [];
|
const parts = safePartitions[key] || [];
|
||||||
const isSelected = parts.some(p => p.id === selectedPartitionId);
|
const isSelected = parts.some(p => p.id === selectedPartitionId);
|
||||||
|
|
||||||
// Реальные размеры ячейки в мм
|
// Реальные размеры
|
||||||
const realW = (x2 - x1) * drawerW;
|
const realW = (x2 - x1) * drawerW;
|
||||||
const realD = (y2 - y1) * drawerD;
|
const realD = (y2 - y1) * drawerD;
|
||||||
|
|
||||||
// --- ЛЕЙБЛ РАЗМЕРА ---
|
// Если перегородок нет - размер по центру
|
||||||
// Если перегородок нет, показываем общий размер по центру
|
|
||||||
if (parts.length === 0) {
|
if (parts.length === 0) {
|
||||||
const labelX = cellX + cellW / 2;
|
const labelX = cellX + cellW / 2;
|
||||||
const labelY = cellY + cellH / 2;
|
const labelY = cellY + cellH / 2;
|
||||||
// Учитываем толщину внешних стенок (или соседних)
|
|
||||||
// Упрощенно: вычитаем толщину (1 раз, т.к. 0.5 с каждой стороны)
|
|
||||||
const textW = Math.max(0, realW - thickness).toFixed(0);
|
const textW = Math.max(0, realW - thickness).toFixed(0);
|
||||||
const textD = Math.max(0, realD - thickness).toFixed(0);
|
const textD = Math.max(0, realD - thickness).toFixed(0);
|
||||||
|
|
||||||
// Рисуем текст только если есть место
|
|
||||||
if (cellH > 40 && cellW > 60) {
|
if (cellH > 40 && cellW > 60) {
|
||||||
elements.push(
|
elements.push(
|
||||||
<text key={`label-${key}`} x={labelX} y={labelY} textAnchor="middle" dominantBaseline="middle" className="fill-slate-400 font-mono text-[10px] pointer-events-none select-none opacity-60">
|
<text key={`label-${key}`} x={labelX} y={labelY} textAnchor="middle" dominantBaseline="middle" className="fill-slate-300 font-mono text-[12px] font-bold pointer-events-none select-none opacity-80" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.8)' }}>
|
||||||
{textW} × {textD}
|
{textW} × {textD}
|
||||||
</text>
|
</text>
|
||||||
);
|
);
|
||||||
@@ -360,11 +357,13 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
|
|
||||||
{parts.map(p => {
|
{parts.map(p => {
|
||||||
const pMin = p.min ?? 0; const pMax = p.max ?? 1;
|
const pMin = p.min ?? 0; const pMax = p.max ?? 1;
|
||||||
let lx1, ly1, lx2, ly2; // Координаты линии
|
let lx1, ly1, lx2, ly2;
|
||||||
let tx, ty; // Координаты текста
|
let tx, ty;
|
||||||
let dist1 = 0, dist2 = 0; // Расстояния до соседей (в мм)
|
let dist1 = 0, dist2 = 0;
|
||||||
|
|
||||||
// Находим соседей для расчета размеров
|
// Используем правильные координаты для поиска соседей
|
||||||
|
// axis='x' -> offset - это X, center - это Y
|
||||||
|
// axis='y' -> offset - это Y, center - это X
|
||||||
const neighbors = getNearestNeighbors(p.offset, (pMin + pMax)/2, p.axis, parts);
|
const neighbors = getNearestNeighbors(p.offset, (pMin + pMax)/2, p.axis, parts);
|
||||||
|
|
||||||
if (p.axis === 'x') {
|
if (p.axis === 'x') {
|
||||||
@@ -372,27 +371,23 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
lx1 = px; lx2 = px;
|
lx1 = px; lx2 = px;
|
||||||
ly1 = cellY + (cellH * pMin); ly2 = cellY + (cellH * pMax);
|
ly1 = cellY + (cellH * pMin); ly2 = cellY + (cellH * pMax);
|
||||||
|
|
||||||
// Размеры слева и справа
|
// Distances Left/Right
|
||||||
const leftRatio = p.offset - neighbors.min;
|
// p.offset is 0..1. neighbor.min is 0..1.
|
||||||
const rightRatio = neighbors.max - p.offset;
|
// Distance = (current - neighbor) * width
|
||||||
dist1 = (leftRatio * realW) - thickness; // Left
|
dist1 = (p.offset - neighbors.min) * realW - thickness;
|
||||||
dist2 = (rightRatio * realW) - thickness; // Right
|
dist2 = (neighbors.max - p.offset) * realW - thickness;
|
||||||
|
|
||||||
tx = px;
|
tx = px; ty = (ly1 + ly2) / 2;
|
||||||
ty = (ly1 + ly2) / 2;
|
|
||||||
} else {
|
} else {
|
||||||
const py = cellY + (cellH * p.offset);
|
const py = cellY + (cellH * p.offset);
|
||||||
ly1 = py; ly2 = py;
|
ly1 = py; ly2 = py;
|
||||||
lx1 = cellX + (cellW * pMin); lx2 = cellX + (cellW * pMax);
|
lx1 = cellX + (cellW * pMin); lx2 = cellX + (cellW * pMax);
|
||||||
|
|
||||||
// Размеры сверху и снизу
|
// Distances Top/Bottom
|
||||||
const topRatio = p.offset - neighbors.min;
|
dist1 = (p.offset - neighbors.min) * realD - thickness;
|
||||||
const botRatio = neighbors.max - p.offset;
|
dist2 = (neighbors.max - p.offset) * realD - thickness;
|
||||||
dist1 = (topRatio * realD) - thickness; // Top
|
|
||||||
dist2 = (botRatio * realD) - thickness; // Bottom
|
|
||||||
|
|
||||||
tx = (lx1 + lx2) / 2;
|
tx = (lx1 + lx2) / 2; ty = py;
|
||||||
ty = py;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSel = selectedPartitionId === p.id;
|
const isSel = selectedPartitionId === p.id;
|
||||||
@@ -400,23 +395,22 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<g key={p.id}>
|
<g key={p.id}>
|
||||||
{/* Сама линия */}
|
|
||||||
<g onDoubleClick={(e) => { e.stopPropagation(); removePartition(key, p.id); }}>
|
<g onDoubleClick={(e) => { e.stopPropagation(); removePartition(key, p.id); }}>
|
||||||
<line x1={lx1} y1={ly1} x2={lx2} y2={ly2} stroke="transparent" strokeWidth="40" />
|
<line x1={lx1} y1={ly1} x2={lx2} y2={ly2} stroke="transparent" strokeWidth="40" />
|
||||||
<line x1={lx1} y1={ly1} x2={lx2} y2={ly2} stroke={isSel ? "#a855f7" : (isHov ? "#d8b4fe" : "#7e22ce")} strokeWidth={isSel ? 6 : 4} strokeLinecap="round" />
|
<line x1={lx1} y1={ly1} x2={lx2} y2={ly2} stroke={isSel ? "#a855f7" : (isHov ? "#d8b4fe" : "#7e22ce")} strokeWidth={isSel ? 6 : 4} strokeLinecap="round" />
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
{/* Текст размеров (Слева/Справа или Сверху/Снизу) */}
|
{/* Размеры (только если есть место) */}
|
||||||
<text x={tx} y={ty} className="pointer-events-none select-none font-mono text-[9px] fill-white font-bold" textAnchor="middle" dominantBaseline="middle">
|
<text x={tx} y={ty} className="pointer-events-none select-none font-mono text-[11px] font-bold fill-white" textAnchor="middle" dominantBaseline="middle" style={{ textShadow: '0px 0px 4px #000' }}>
|
||||||
{p.axis === 'x' ? (
|
{p.axis === 'x' ? (
|
||||||
<>
|
<>
|
||||||
<tspan dx="-12" fill="#94a3b8">{dist1.toFixed(0)}</tspan>
|
<tspan dx="-16" fill="#cbd5e1">{Math.max(0, dist1).toFixed(0)}</tspan>
|
||||||
<tspan dx="24" fill="#94a3b8">{dist2.toFixed(0)}</tspan>
|
<tspan dx="32" fill="#cbd5e1">{Math.max(0, dist2).toFixed(0)}</tspan>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<tspan x={tx} dy="-8" fill="#94a3b8">{dist1.toFixed(0)}</tspan>
|
<tspan x={tx} dy="-10" fill="#cbd5e1">{Math.max(0, dist1).toFixed(0)}</tspan>
|
||||||
<tspan x={tx} dy="16" fill="#94a3b8">{dist2.toFixed(0)}</tspan>
|
<tspan x={tx} dy="20" fill="#cbd5e1">{Math.max(0, dist2).toFixed(0)}</tspan>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</text>
|
</text>
|
||||||
@@ -480,7 +474,7 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
>
|
>
|
||||||
<svg ref={svgRef} viewBox={`0 0 ${viewBoxW} ${viewBoxH}`} className="w-full h-full touch-none block"
|
<svg ref={svgRef} viewBox={`0 0 ${viewBoxW} ${viewBoxH}`} className="w-full h-full touch-none block"
|
||||||
preserveAspectRatio="none"
|
preserveAspectRatio="none"
|
||||||
onMouseMove={handleMouseMove} onMouseDown={handleMouseDown} onMouseUp={() => setDragging(null)} onMouseLeave={() => setDragging(null)} onContextMenu={(e) => e.preventDefault()}
|
onMouseMove={handleGlobalMouseMove} onMouseDown={handleMouseDown} onMouseUp={() => setDragging(null)} onMouseLeave={() => setDragging(null)} onContextMenu={(e) => e.preventDefault()}
|
||||||
>
|
>
|
||||||
<defs><pattern id="grid" width="50" height="50" patternUnits="userSpaceOnUse"><path d="M 50 0 L 0 0 0 50" fill="none" stroke="rgba(255,255,255,0.03)" strokeWidth="1"/></pattern></defs>
|
<defs><pattern id="grid" width="50" height="50" patternUnits="userSpaceOnUse"><path d="M 50 0 L 0 0 0 50" fill="none" stroke="rgba(255,255,255,0.03)" strokeWidth="1"/></pattern></defs>
|
||||||
<rect width="100%" height="100%" fill="url(#grid)" />
|
<rect width="100%" height="100%" fill="url(#grid)" />
|
||||||
@@ -500,9 +494,8 @@ export const LayoutStep: React.FC<Props> = ({ config, splits, onChange }) => {
|
|||||||
</g>
|
</g>
|
||||||
))}
|
))}
|
||||||
{/* Phantoms */}
|
{/* Phantoms */}
|
||||||
{mode === 'lines' && !hoveredMainSplit && !dragging && phantomMainAxis && (
|
{mode === 'lines' && !hoveredMainSplit && !dragging && phantomMainAxis === 'x' && <line x1={mousePos.x * viewBoxW} y1="0" x2={mousePos.x * viewBoxW} y2="100%" stroke="#3b82f6" strokeWidth="4" strokeDasharray="8,8" className="pointer-events-none opacity-50"/>}
|
||||||
<line x1={phantomMainAxis === 'x' ? mousePos.x * viewBoxW : 0} y1={phantomMainAxis === 'y' ? mousePos.y * viewBoxH : 0} x2={phantomMainAxis === 'x' ? mousePos.x * viewBoxW : '100%'} y2={phantomMainAxis === 'y' ? mousePos.y * viewBoxH : '100%'} stroke="#3b82f6" strokeWidth="4" strokeDasharray="8,8" className="pointer-events-none opacity-50"/>
|
{mode === 'lines' && !hoveredMainSplit && !dragging && phantomMainAxis === 'y' && <line x1="0" y1={mousePos.y * viewBoxH} x2="100%" y2={mousePos.y * viewBoxH} stroke="#3b82f6" strokeWidth="4" strokeDasharray="8,8" className="pointer-events-none opacity-50"/>}
|
||||||
)}
|
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user