refactor(NetworkMapUnifi): simplify curve point calculations and improve label positioning to enhance edge rendering and avoid collisions
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m58s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m58s
This commit is contained in:
@@ -182,10 +182,8 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
|
|||||||
}).filter((e) => e.p1 && e.p2);
|
}).filter((e) => e.p1 && e.p2);
|
||||||
}, [connections, positions, pingMap]);
|
}, [connections, positions, pingMap]);
|
||||||
|
|
||||||
// Вычисляем старт/финиш и контрольную точку так, чтобы:
|
// Вычисляем старт/финиш и одну контрольную точку:
|
||||||
// - рёбра выходили из карточек строго под 90° (горизонтально/вертикально),
|
// рёбра выходят из карточек под 90°, дальше сразу плавная дуга без прямых сегментов.
|
||||||
// - рядом с узлами был короткий прямой участок,
|
|
||||||
// - далее шла плавная дуга (как в UniFi).
|
|
||||||
const computeCurvePoints = useCallback((e) => {
|
const computeCurvePoints = useCallback((e) => {
|
||||||
const { p1, p2 } = e;
|
const { p1, p2 } = e;
|
||||||
const dx = p2.x - p1.x;
|
const dx = p2.x - p1.x;
|
||||||
@@ -215,40 +213,28 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
|
|||||||
const ddx = ex - sx;
|
const ddx = ex - sx;
|
||||||
const ddy = ey - sy;
|
const ddy = ey - sy;
|
||||||
const len = Math.hypot(ddx, ddy) || 1;
|
const len = Math.hypot(ddx, ddy) || 1;
|
||||||
const ux = ddx / len;
|
|
||||||
const uy = ddy / len;
|
|
||||||
|
|
||||||
// Длина прямого участка и величина изгиба зависят от расстояния между узлами,
|
const mx = (sx + ex) / 2;
|
||||||
// чтобы при маленьких расстояниях не было «ломаных» и самопересечений.
|
const my = (sy + ey) / 2;
|
||||||
const stub = Math.min(22, len / 3);
|
|
||||||
|
|
||||||
// Точки начала/конца дуги (после прямых участков)
|
|
||||||
const csx = sx + ux * stub;
|
|
||||||
const csy = sy + uy * stub;
|
|
||||||
const cex = ex - ux * stub;
|
|
||||||
const cey = ey - uy * stub;
|
|
||||||
|
|
||||||
const mx = (csx + cex) / 2;
|
|
||||||
const my = (csy + cey) / 2;
|
|
||||||
const baseOffset = Math.min(40, len / 2);
|
const baseOffset = Math.min(40, len / 2);
|
||||||
const perpX = (-ddy / len) * baseOffset;
|
const perpX = (-ddy / len) * baseOffset;
|
||||||
const perpY = (ddx / len) * baseOffset;
|
const perpY = (ddx / len) * baseOffset;
|
||||||
const cpx = mx + perpX;
|
const cpx = mx + perpX;
|
||||||
const cpy = my + perpY;
|
const cpy = my + perpY;
|
||||||
|
|
||||||
return { sx, sy, ex, ey, csx, csy, cex, cey, cpx, cpy };
|
return { sx, sy, ex, ey, cpx, cpy };
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const pathD = useCallback((e) => {
|
const pathD = useCallback((e) => {
|
||||||
const { sx, sy, ex, ey, csx, csy, cex, cey, cpx, cpy } = computeCurvePoints(e);
|
const { sx, sy, ex, ey, cpx, cpy } = computeCurvePoints(e);
|
||||||
return `M ${sx} ${sy} L ${csx} ${csy} Q ${cpx} ${cpy} ${cex} ${cey} L ${ex} ${ey}`;
|
return `M ${sx} ${sy} Q ${cpx} ${cpy} ${ex} ${ey}`;
|
||||||
}, [computeCurvePoints]);
|
}, [computeCurvePoints]);
|
||||||
|
|
||||||
const labelPoint = useCallback((e) => {
|
const labelPoint = useCallback((e) => {
|
||||||
const { csx, csy, cex, cey, cpx, cpy } = computeCurvePoints(e);
|
const { sx, sy, ex, ey, cpx, cpy } = computeCurvePoints(e);
|
||||||
const t = 0.5;
|
const t = 0.5;
|
||||||
const lx = (1 - t) * (1 - t) * csx + 2 * (1 - t) * t * cpx + t * t * cex;
|
const lx = (1 - t) * (1 - t) * sx + 2 * (1 - t) * t * cpx + t * t * ex;
|
||||||
const ly = (1 - t) * (1 - t) * csy + 2 * (1 - t) * t * cpy + t * t * cey;
|
const ly = (1 - t) * (1 - t) * sy + 2 * (1 - t) * t * cpy + t * t * ey;
|
||||||
return { x: lx, y: ly };
|
return { x: lx, y: ly };
|
||||||
}, [computeCurvePoints]);
|
}, [computeCurvePoints]);
|
||||||
|
|
||||||
@@ -279,7 +265,12 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
|
|||||||
>
|
>
|
||||||
{/* Рёбра: зелёные кривые (стиль UNIFI — проводные соединения) */}
|
{/* Рёбра: зелёные кривые (стиль UNIFI — проводные соединения) */}
|
||||||
<g>
|
<g>
|
||||||
{edgesWithPing.map((e, idx) => (
|
{(() => {
|
||||||
|
const used = [];
|
||||||
|
const LABEL_W = 64;
|
||||||
|
const LABEL_H = 20;
|
||||||
|
const LABEL_GAP = 4;
|
||||||
|
return edgesWithPing.map((e, idx) => (
|
||||||
<g key={`${e.from}-${e.to}-${idx}`}>
|
<g key={`${e.from}-${e.to}-${idx}`}>
|
||||||
<path
|
<path
|
||||||
d={pathD(e)}
|
d={pathD(e)}
|
||||||
@@ -289,16 +280,35 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
|
|||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
/>
|
/>
|
||||||
{e.pingLabel && (() => {
|
{e.pingLabel && (() => {
|
||||||
const lp = labelPoint(e);
|
const base = labelPoint(e);
|
||||||
|
let x = base.x;
|
||||||
|
let y = base.y;
|
||||||
|
// Простейшее разруливание коллизий: если рядом уже есть бейдж,
|
||||||
|
// сдвигаем вниз на LABEL_H + GAP, пока не найдём свободное место.
|
||||||
|
let level = 0;
|
||||||
|
// ограничиваем число итераций, чтобы не зависнуть
|
||||||
|
while (
|
||||||
|
level < 10 &&
|
||||||
|
used.some(
|
||||||
|
(p) =>
|
||||||
|
Math.abs(p.x - x) < LABEL_W &&
|
||||||
|
Math.abs(p.y - (y + level * (LABEL_H + LABEL_GAP))) < LABEL_H
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
level += 1;
|
||||||
|
}
|
||||||
|
y = y + level * (LABEL_H + LABEL_GAP);
|
||||||
|
used.push({ x, y });
|
||||||
|
|
||||||
const pad = 6;
|
const pad = 6;
|
||||||
const w = e.pingLabel.length * 6 + pad * 2;
|
const w = e.pingLabel.length * 6 + pad * 2;
|
||||||
const h = 18;
|
const h = 18;
|
||||||
return (
|
return (
|
||||||
<g>
|
<g>
|
||||||
<rect x={lp.x - w / 2} y={lp.y - h / 2} width={w} height={h} rx={4} fill="rgba(15,23,42,0.9)" stroke={UNIFI_GREEN} strokeWidth={1} />
|
<rect x={x - w / 2} y={y - h / 2} width={w} height={h} rx={4} fill="rgba(15,23,42,0.9)" stroke={UNIFI_GREEN} strokeWidth={1} />
|
||||||
<text
|
<text
|
||||||
x={lp.x}
|
x={x}
|
||||||
y={lp.y}
|
y={y}
|
||||||
textAnchor="middle"
|
textAnchor="middle"
|
||||||
dominantBaseline="middle"
|
dominantBaseline="middle"
|
||||||
fill={UNIFI_GREEN}
|
fill={UNIFI_GREEN}
|
||||||
@@ -311,7 +321,8 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
|
|||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
</g>
|
</g>
|
||||||
))}
|
));
|
||||||
|
})()}
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
{/* Узлы: карточки серверов в стиле UNIFI (перетаскиваемые) */}
|
{/* Узлы: карточки серверов в стиле UNIFI (перетаскиваемые) */}
|
||||||
|
|||||||
Reference in New Issue
Block a user