Files
lan-manager/web/node_modules/@antv/g6-element/es/edges/polyline-util.js
openclaw 0a5f6a8047 Initial commit: Lan-manager project code
- Go backend (server/)
- Frontend (web/, server/static/)
- Database and deployment files
- Scripts and docs

Co-Authored-By: 狸花猫/Claude-Qwen3.6-Plus 🐾
2026-04-20 00:52:58 +08:00

621 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
export var getBBoxFromPoint = function getBBoxFromPoint(point) {
var x = point.x,
y = point.y;
return {
x: x,
y: y,
centerX: x,
centerY: y,
minX: x,
minY: y,
maxX: x,
maxY: y,
height: 0,
width: 0
};
};
export var getBBoxFromPoints = function getBBoxFromPoints(points) {
if (points === void 0) {
points = [];
}
var xs = [];
var ys = [];
points.forEach(function (p) {
xs.push(p.x);
ys.push(p.y);
});
var minX = Math.min.apply(Math, xs);
var maxX = Math.max.apply(Math, xs);
var minY = Math.min.apply(Math, ys);
var maxY = Math.max.apply(Math, ys);
return {
centerX: (minX + maxX) / 2,
centerY: (minY + maxY) / 2,
maxX: maxX,
maxY: maxY,
minX: minX,
minY: minY,
height: maxY - minY,
width: maxX - minX
};
};
export var isBBoxesOverlapping = function isBBoxesOverlapping(b1, b2) {
return Math.abs(b1.centerX - b2.centerX) * 2 < b1.width + b2.width && Math.abs(b1.centerY - b2.centerY) * 2 < b1.height + b2.height;
};
export var filterConnectPoints = function filterConnectPoints(points) {
// pre-process: remove duplicated points
var result = [];
var map = {};
var pointsLength = points.length;
for (var i = pointsLength - 1; i >= 0; i--) {
var p = points[i];
p.id = "".concat(p.x, "|||").concat(p.y);
if (!map[p.id]) {
map[p.id] = p;
result.push(p);
}
}
return result;
};
export var simplifyPolyline = function simplifyPolyline(points) {
return filterConnectPoints(points);
};
export var getSimplePolyline = function getSimplePolyline(sPoint, tPoint) {
return [sPoint, {
x: sPoint.x,
y: tPoint.y
}, tPoint];
};
export var getExpandedBBox = function getExpandedBBox(bbox, offset) {
if (bbox.width || bbox.height) {
return {
centerX: bbox.centerX,
centerY: bbox.centerY,
minX: bbox.minX - offset,
minY: bbox.minY - offset,
maxX: bbox.maxX + offset,
maxY: bbox.maxY + offset,
height: bbox.height + 2 * offset,
width: bbox.width + 2 * offset
};
}
// when it is a point
return bbox;
};
export var isHorizontalPort = function isHorizontalPort(port, bbox) {
var dx = Math.abs(port.x - bbox.centerX);
var dy = Math.abs(port.y - bbox.centerY);
if (dx === 0 && dy === 0) return 0;
return dx / bbox.width > dy / bbox.height;
};
export var getExpandedBBoxPoint = function getExpandedBBoxPoint(bbox,
// 将原来节点 bbox 扩展了 offset 后的 bbox且被 gridSize 格式化
point,
// 被 gridSize 格式化后的位置anchorPoint
anotherPoint) {
var isHorizontal = isHorizontalPort(point, bbox);
if (isHorizontal === 0) {
// 说明锚点是节点中心linkCenter: true。需要根据两个节点的相对关系决定方向
var x = bbox.centerX;
var y = bbox.centerY;
if (anotherPoint.y < point.y) {
// 另一端在左上/右上方时,总是从上方走
y = bbox.minY;
} else if (anotherPoint.x > point.x) {
// 另一端在右下方,往右边走
x = bbox.maxX;
} else if (anotherPoint.x < point.x) {
// 另一端在左下方,往左边走
x = bbox.minX;
} else if (anotherPoint.x === point.x) {
// 另一段在正下方,往下走
y = bbox.maxY;
}
return {
x: x,
y: y
};
}
if (isHorizontal) {
return {
x: point.x > bbox.centerX ? bbox.maxX : bbox.minX,
y: point.y
};
}
return {
x: point.x,
y: point.y > bbox.centerY ? bbox.maxY : bbox.minY
};
};
/**
*
* @param b1
* @param b2
*/
export var mergeBBox = function mergeBBox(b1, b2) {
var minX = Math.min(b1.minX, b2.minX);
var minY = Math.min(b1.minY, b2.minY);
var maxX = Math.max(b1.maxX, b2.maxX);
var maxY = Math.max(b1.maxY, b2.maxY);
return {
centerX: (minX + maxX) / 2,
centerY: (minY + maxY) / 2,
minX: minX,
minY: minY,
maxX: maxX,
maxY: maxY,
height: maxY - minY,
width: maxX - minX
};
};
export var getPointsFromBBox = function getPointsFromBBox(bbox) {
// anticlockwise
return [{
x: bbox.minX,
y: bbox.minY
}, {
x: bbox.maxX,
y: bbox.minY
}, {
x: bbox.maxX,
y: bbox.maxY
}, {
x: bbox.minX,
y: bbox.maxY
}];
};
export var isPointOutsideBBox = function isPointOutsideBBox(point, bbox) {
var x = point.x,
y = point.y;
return x < bbox.minX || x > bbox.maxX || y < bbox.minY || y > bbox.maxY;
};
export var getBBoxXCrossPoints = function getBBoxXCrossPoints(bbox, x) {
if (x < bbox.minX || x > bbox.maxX) {
return [];
}
return [{
x: x,
y: bbox.minY
}, {
x: x,
y: bbox.maxY
}];
};
export var getBBoxYCrossPoints = function getBBoxYCrossPoints(bbox, y) {
if (y < bbox.minY || y > bbox.maxY) {
return [];
}
return [{
x: bbox.minX,
y: y
}, {
x: bbox.maxX,
y: y
}];
};
export var getBBoxCrossPointsByPoint = function getBBoxCrossPointsByPoint(bbox, point) {
return getBBoxXCrossPoints(bbox, point.x).concat(getBBoxYCrossPoints(bbox, point.y));
};
/**
* 曼哈顿距离
*/
export var distance = function distance(p1, p2) {
return Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
};
/**
* 如果 points 中的一个节点 x 与 p 相等,则消耗 -2。y 同
* 即优先选择和 points 在同一水平线 / 垂直线上的点
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export var _costByPoints = function _costByPoints(p, points) {
var offset = -2;
var result = 0;
points.forEach(function (point) {
if (point) {
if (p.x === point.x) {
result += offset;
}
if (p.y === point.y) {
result += offset;
}
}
});
return result;
};
/**
* ps 经过 p 到 pt 的距离,减去其他路过节点造成的消耗
*/
export var heuristicCostEstimate = function heuristicCostEstimate(p, ps, pt, source, target) {
return distance(p, ps) + distance(p, pt) + _costByPoints(p, [ps, pt, source, target]);
};
var _reconstructPath = function reconstructPath(pathPoints, pointById, cameFrom, currentId, iterator) {
if (iterator === void 0) {
iterator = 0;
}
pathPoints.unshift(pointById[currentId]);
if (cameFrom[currentId] && cameFrom[currentId] !== currentId && iterator <= 100) {
_reconstructPath(pathPoints, pointById, cameFrom, cameFrom[currentId], iterator + 1);
}
};
/**
* 从 arr 中删去 item
*/
export { _reconstructPath as reconstructPath };
export var removeFrom = function removeFrom(arr, item) {
var index = arr.indexOf(item);
if (index > -1) {
arr.splice(index, 1);
}
};
export var isSegmentsIntersected = function isSegmentsIntersected(p0, p1, p2, p3) {
var v1x = p2.x - p0.x;
var v1y = p2.y - p0.y;
var v2x = p3.x - p0.x;
var v2y = p3.y - p0.y;
var v3x = p2.x - p1.x;
var v3y = p2.y - p1.y;
var v4x = p3.x - p1.x;
var v4y = p3.y - p1.y;
var pd1 = v1x * v2y - v1y * v2x;
var pd2 = v3x * v4y - v3y * v4x;
var pd3 = v1x * v3y - v1y * v3x;
var pd4 = v2x * v4y - v2y * v4x;
return pd1 * pd2 <= 0 && pd3 * pd4 <= 0;
};
export var isSegmentCrossingBBox = function isSegmentCrossingBBox(p1, p2, bbox) {
if (bbox.width || bbox.height) {
var _a = getPointsFromBBox(bbox),
pa = _a[0],
pb = _a[1],
pc = _a[2],
pd = _a[3];
return isSegmentsIntersected(p1, p2, pa, pb) || isSegmentsIntersected(p1, p2, pa, pd) || isSegmentsIntersected(p1, p2, pb, pc) || isSegmentsIntersected(p1, p2, pc, pd);
}
return false;
};
/**
* 在 points 中找到满足 x 或 y 和 point 的 x 或 y 相等,且与 point 连线不经过 bbox1 与 bbox2 的点
*/
export var getNeighborPoints = function getNeighborPoints(points, point, bbox1, bbox2) {
var neighbors = [];
points.forEach(function (p) {
if (p === point) return;
if (p.x === point.x || p.y === point.y) {
if (isSegmentCrossingBBox(p, point, bbox1) || isSegmentCrossingBBox(p, point, bbox2)) return;
neighbors.push(p);
}
});
return filterConnectPoints(neighbors);
};
/**
* sorted array ascendly
* add new item to proper index when calling add
*/
var SortedArray = /** @class */function () {
function SortedArray() {
this.arr = [];
this.map = {};
this.arr = [];
this.map = {};
}
SortedArray.prototype._innerAdd = function (item, length) {
var idxRange = [0, length - 1];
while (idxRange[1] - idxRange[0] > 1) {
var midIdx = Math.floor((idxRange[0] + idxRange[1]) / 2);
if (this.arr[midIdx].value > item.value) {
idxRange[1] = midIdx;
} else if (this.arr[midIdx].value < item.value) {
idxRange[0] = midIdx;
} else {
this.arr.splice(midIdx, 0, item);
this.map[item.id] = true;
return;
}
}
this.arr.splice(idxRange[1], 0, item);
this.map[item.id] = true;
};
SortedArray.prototype.add = function (item) {
// 已经存在,先移除
delete this.map[item.id];
var length = this.arr.length;
if (!length) {
this.arr.push(item);
this.map[item.id] = true;
return;
}
// 比最后一个大,加入尾部
if (this.arr[length - 1].value < item.value) {
this.arr.push(item);
this.map[item.id] = true;
return;
}
this._innerAdd(item, length);
};
// only remove from the map to avoid cost
// clear the invalid (not in the map) item when calling minId(true)
SortedArray.prototype.remove = function (id) {
if (!this.map[id]) return;
delete this.map[id];
};
SortedArray.prototype._clearAndGetMinId = function () {
var res;
for (var i = this.arr.length - 1; i >= 0; i--) {
if (this.map[this.arr[i].id]) res = this.arr[i].id;else this.arr.splice(i, 1);
}
return res;
};
SortedArray.prototype._findFirstId = function () {
while (this.arr.length) {
var first = this.arr.shift();
if (this.map[first.id]) return first.id;
}
};
SortedArray.prototype.minId = function (clear) {
if (clear) {
return this._clearAndGetMinId();
} else {
return this._findFirstId();
}
};
return SortedArray;
}();
export { SortedArray };
export var pathFinder = function pathFinder(points, start, goal, sBBox, tBBox, os, ot) {
var _a;
// A-Star Algorithm
var closedSet = [];
var openSet = (_a = {}, _a[start.id] = start, _a);
var cameFrom = {};
var gScore = {}; // all default values are Infinity
var fScore = {}; // all default values are Infinity
gScore[start.id] = 0;
fScore[start.id] = heuristicCostEstimate(start, goal, start);
var sortedOpenSet = new SortedArray();
sortedOpenSet.add({
id: start.id,
value: fScore[start.id]
});
var pointById = {};
points.forEach(function (p) {
pointById[p.id] = p;
});
var current;
while (Object.keys(openSet).length) {
var minId = sortedOpenSet.minId(false);
if (minId) {
current = openSet[minId];
} else {
break;
}
// 若 openSet 中 fScore 最小的点就是终点
if (current === goal) {
// ending condition
var pathPoints = [];
_reconstructPath(pathPoints, pointById, cameFrom, goal.id);
return pathPoints;
}
delete openSet[current.id];
sortedOpenSet.remove(current.id);
closedSet.push(current);
var neighborPoints = getNeighborPoints(points, current, sBBox, tBBox);
var iterateNeighbors = function iterateNeighbors(items) {
items.forEach(function (neighbor) {
if (closedSet.indexOf(neighbor) !== -1) {
return;
}
var neighborId = neighbor.id;
if (!openSet[neighborId]) {
openSet[neighborId] = neighbor;
}
var tentativeGScore = fScore[current.id] + distance(current, neighbor); // + distance(neighbor, goal);
if (gScore[neighborId] && tentativeGScore >= gScore[neighborId]) {
sortedOpenSet.add({
id: neighborId,
value: fScore[neighborId]
});
return;
}
cameFrom[neighborId] = current.id;
gScore[neighborId] = tentativeGScore;
fScore[neighborId] = gScore[neighborId] + heuristicCostEstimate(neighbor, goal, start, os, ot);
sortedOpenSet.add({
id: neighborId,
value: fScore[neighborId]
});
});
};
iterateNeighbors(neighborPoints);
}
// throw new Error('Cannot find path');
return [start, goal];
};
export var isBending = function isBending(p0, p1, p2) {
return !(p0.x === p1.x && p1.x === p2.x || p0.y === p1.y && p1.y === p2.y);
};
export var getBorderRadiusPoints = function getBorderRadiusPoints(p0, p1, p2, r) {
var d0 = distance(p0, p1);
var d1 = distance(p2, p1);
if (d0 < r) {
r = d0;
}
if (d1 < r) {
r = d1;
}
var ps = {
x: p1.x - r / d0 * (p1.x - p0.x),
y: p1.y - r / d0 * (p1.y - p0.y)
};
var pt = {
x: p1.x - r / d1 * (p1.x - p2.x),
y: p1.y - r / d1 * (p1.y - p2.y)
};
return [ps, pt];
};
export var getPathWithBorderRadiusByPolyline = function getPathWithBorderRadiusByPolyline(points, borderRadius) {
var pathSegments = [];
var startPoint = points[0];
pathSegments.push("M".concat(startPoint.x, " ").concat(startPoint.y));
points.forEach(function (p, i) {
var p1 = points[i + 1];
var p2 = points[i + 2];
if (p1 && p2) {
if (isBending(p, p1, p2)) {
var _a = getBorderRadiusPoints(p, p1, p2, borderRadius),
ps = _a[0],
pt = _a[1];
pathSegments.push("L".concat(ps.x, " ").concat(ps.y));
pathSegments.push("Q".concat(p1.x, " ").concat(p1.y, " ").concat(pt.x, " ").concat(pt.y));
pathSegments.push("L".concat(pt.x, " ").concat(pt.y));
} else {
pathSegments.push("L".concat(p1.x, " ").concat(p1.y));
}
} else if (p1) {
pathSegments.push("L".concat(p1.x, " ").concat(p1.y));
}
});
return pathSegments.join('');
};
export var getPolylinePoints = function getPolylinePoints(start, end, sNode, tNode, offset) {
var sBBox, tBBox;
if (!sNode || !sNode.getType()) {
sBBox = getBBoxFromPoint(start);
} else if (sNode.getType() === 'combo') {
var sKeyShapeBBox = sNode.getKeyShape().getBBox();
if (sKeyShapeBBox) {
var _a = sNode.getModel(),
sx = _a.x,
sy = _a.y;
sBBox = {
x: sx,
y: sy,
width: sKeyShapeBBox.width,
height: sKeyShapeBBox.height,
minX: sKeyShapeBBox.minX + sx,
maxX: sKeyShapeBBox.maxX + sx,
minY: sKeyShapeBBox.minY + sy,
maxY: sKeyShapeBBox.maxY + sy
};
sBBox.centerX = (sBBox.minX + sBBox.maxX) / 2;
sBBox.centerY = (sBBox.minY + sBBox.maxY) / 2;
} else {
sBBox = getBBoxFromPoint(start);
}
} else {
sBBox = sNode && sNode.getBBox();
}
if (!tNode || !tNode.getType()) {
tBBox = getBBoxFromPoint(end);
} else if (tNode.getType() === 'combo') {
var tKeyShapeBBox = tNode.getKeyShape().getBBox();
if (tKeyShapeBBox) {
var _b = tNode.getModel(),
tx = _b.x,
ty = _b.y;
tBBox = {
x: tx,
y: ty,
width: tKeyShapeBBox.width,
height: tKeyShapeBBox.height,
minX: tKeyShapeBBox.minX + tx,
maxX: tKeyShapeBBox.maxX + tx,
minY: tKeyShapeBBox.minY + ty,
maxY: tKeyShapeBBox.maxY + ty
};
tBBox.centerX = (tBBox.minX + tBBox.maxX) / 2;
tBBox.centerY = (tBBox.minY + tBBox.maxY) / 2;
} else {
tBBox = getBBoxFromPoint(end);
}
} else {
tBBox = tNode && tNode.getBBox();
}
// if (isBBoxesOverlapping(sBBox, tBBox)) {
// // source and target nodes are overlapping
// return simplifyPolyline(getSimplePolyline(start, end));
// }
var sxBBox = getExpandedBBox(sBBox, offset);
var txBBox = getExpandedBBox(tBBox, offset);
// if (isBBoxesOverlapping(sxBBox, txBBox)) {
// // the expanded bounding boxes of source and target nodes are overlapping
// return simplifyPolyline(getSimplePolyline(start, end));
// }
var sPoint = getExpandedBBoxPoint(sxBBox, start, end);
var tPoint = getExpandedBBoxPoint(txBBox, end, start);
var lineBBox = getBBoxFromPoints([sPoint, tPoint]);
var sMixBBox = mergeBBox(sxBBox, lineBBox);
var tMixBBox = mergeBBox(txBBox, lineBBox);
var connectPoints = [];
connectPoints = connectPoints.concat(getPointsFromBBox(sMixBBox)).concat(getPointsFromBBox(tMixBBox));
var centerPoint = {
x: (start.x + end.x) / 2,
y: (start.y + end.y) / 2
};
[lineBBox, sMixBBox, tMixBBox].forEach(function (bbox) {
connectPoints = connectPoints.concat(getBBoxCrossPointsByPoint(bbox, centerPoint).filter(function (p) {
return isPointOutsideBBox(p, sxBBox) && isPointOutsideBBox(p, txBBox);
}));
});
[{
x: sPoint.x,
y: tPoint.y
}, {
x: tPoint.x,
y: sPoint.y
}].forEach(function (p) {
// impossible!!
if (isPointOutsideBBox(p, sxBBox) && isPointOutsideBBox(p, txBBox) // &&
// isPointInsideBBox(p, sMixBBox) && isPointInsideBBox(p, tMixBBox)
) {
connectPoints.push(p);
}
});
connectPoints.unshift(sPoint);
connectPoints.push(tPoint);
// filter out dulplicated points in connectPoints
connectPoints = filterConnectPoints(connectPoints); // , sxBBox, txBBox, outerBBox
var pathPoints = pathFinder(connectPoints, sPoint, tPoint, sBBox, tBBox, start, end);
pathPoints.unshift(start);
pathPoints.push(end);
return simplifyPolyline(pathPoints);
};
/**
* 去除连续同 x 不同 y 的中间点;去除连续同 y 不同 x 的中间点
* @param points 坐标集合 { x: number, y: number, id: string }[]
* @returns
*/
export var removeRedundantPoint = function removeRedundantPoint(points) {
if (!(points === null || points === void 0 ? void 0 : points.length)) return points;
var beginPoint = points[points.length - 1];
var current = {
x: beginPoint.x,
y: beginPoint.y
};
var continueSameX = [beginPoint];
var continueSameY = [beginPoint];
for (var i = points.length - 2; i >= 0; i--) {
var point = points[i];
if (point.x === current.x) {
continueSameX.push(point);
} else {
continueSameX = [point];
current.x = point.x;
}
if (point.y === current.y) {
continueSameY.push(point);
} else {
continueSameY = [point];
current.y = point.y;
}
if (continueSameX.length > 2) {
var removeIdx = points.indexOf(continueSameX[1]);
if (removeIdx > -1) points.splice(removeIdx, 1);
continue;
}
if (continueSameY.length > 2) {
var removeIdx = points.indexOf(continueSameY[1]);
if (removeIdx > -1) points.splice(removeIdx, 1);
}
}
return points;
};