From c9babc163419ceafed85479cadce5812553327cf Mon Sep 17 00:00:00 2001 From: shats Date: Tue, 17 Feb 2026 17:35:03 +0700 Subject: [PATCH] refactor(NetworkMapUnifi): simplify curve point calculations and improve label positioning to enhance edge rendering and avoid collisions --- frontend/src/NetworkMapUnifi.jsx | 71 ++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/frontend/src/NetworkMapUnifi.jsx b/frontend/src/NetworkMapUnifi.jsx index 95cdc22..b76810a 100644 --- a/frontend/src/NetworkMapUnifi.jsx +++ b/frontend/src/NetworkMapUnifi.jsx @@ -182,10 +182,8 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa }).filter((e) => e.p1 && e.p2); }, [connections, positions, pingMap]); - // Вычисляем старт/финиш и контрольную точку так, чтобы: - // - рёбра выходили из карточек строго под 90° (горизонтально/вертикально), - // - рядом с узлами был короткий прямой участок, - // - далее шла плавная дуга (как в UniFi). + // Вычисляем старт/финиш и одну контрольную точку: + // рёбра выходят из карточек под 90°, дальше сразу плавная дуга без прямых сегментов. const computeCurvePoints = useCallback((e) => { const { p1, p2 } = e; const dx = p2.x - p1.x; @@ -215,40 +213,28 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa const ddx = ex - sx; const ddy = ey - sy; const len = Math.hypot(ddx, ddy) || 1; - const ux = ddx / len; - const uy = ddy / len; - // Длина прямого участка и величина изгиба зависят от расстояния между узлами, - // чтобы при маленьких расстояниях не было «ломаных» и самопересечений. - 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 mx = (sx + ex) / 2; + const my = (sy + ey) / 2; const baseOffset = Math.min(40, len / 2); const perpX = (-ddy / len) * baseOffset; const perpY = (ddx / len) * baseOffset; const cpx = mx + perpX; 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 { sx, sy, ex, ey, csx, csy, cex, cey, cpx, cpy } = computeCurvePoints(e); - return `M ${sx} ${sy} L ${csx} ${csy} Q ${cpx} ${cpy} ${cex} ${cey} L ${ex} ${ey}`; + const { sx, sy, ex, ey, cpx, cpy } = computeCurvePoints(e); + return `M ${sx} ${sy} Q ${cpx} ${cpy} ${ex} ${ey}`; }, [computeCurvePoints]); 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 lx = (1 - t) * (1 - t) * csx + 2 * (1 - t) * t * cpx + t * t * cex; - const ly = (1 - t) * (1 - t) * csy + 2 * (1 - t) * t * cpy + t * t * cey; + const lx = (1 - t) * (1 - t) * sx + 2 * (1 - t) * t * cpx + t * t * ex; + const ly = (1 - t) * (1 - t) * sy + 2 * (1 - t) * t * cpy + t * t * ey; return { x: lx, y: ly }; }, [computeCurvePoints]); @@ -279,7 +265,12 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa > {/* Рёбра: зелёные кривые (стиль UNIFI — проводные соединения) */} - {edgesWithPing.map((e, idx) => ( + {(() => { + const used = []; + const LABEL_W = 64; + const LABEL_H = 20; + const LABEL_GAP = 4; + return edgesWithPing.map((e, idx) => ( {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 w = e.pingLabel.length * 6 + pad * 2; const h = 18; return ( - + - ))} + )); + })()} {/* Узлы: карточки серверов в стиле UNIFI (перетаскиваемые) */}