Files
lan-manager/web/node_modules/@antv/g6-element/lib/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

625 lines
20 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.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.simplifyPolyline = exports.removeRedundantPoint = exports.removeFrom = exports.reconstructPath = exports.pathFinder = exports.mergeBBox = exports.isSegmentsIntersected = exports.isSegmentCrossingBBox = exports.isPointOutsideBBox = exports.isHorizontalPort = exports.isBending = exports.isBBoxesOverlapping = exports.heuristicCostEstimate = exports.getSimplePolyline = exports.getPolylinePoints = exports.getPointsFromBBox = exports.getPathWithBorderRadiusByPolyline = exports.getNeighborPoints = exports.getExpandedBBoxPoint = exports.getExpandedBBox = exports.getBorderRadiusPoints = exports.getBBoxYCrossPoints = exports.getBBoxXCrossPoints = exports.getBBoxFromPoints = exports.getBBoxFromPoint = exports.getBBoxCrossPointsByPoint = exports.filterConnectPoints = exports.distance = exports._costByPoints = exports.SortedArray = void 0;
var getBBoxFromPoint = exports.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
};
};
var getBBoxFromPoints = exports.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
};
};
var isBBoxesOverlapping = exports.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;
};
var filterConnectPoints = exports.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;
};
var simplifyPolyline = exports.simplifyPolyline = function simplifyPolyline(points) {
return filterConnectPoints(points);
};
var getSimplePolyline = exports.getSimplePolyline = function getSimplePolyline(sPoint, tPoint) {
return [sPoint, {
x: sPoint.x,
y: tPoint.y
}, tPoint];
};
var getExpandedBBox = exports.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;
};
var isHorizontalPort = exports.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;
};
var getExpandedBBoxPoint = exports.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
*/
var mergeBBox = exports.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
};
};
var getPointsFromBBox = exports.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
}];
};
var isPointOutsideBBox = exports.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;
};
var getBBoxXCrossPoints = exports.getBBoxXCrossPoints = function getBBoxXCrossPoints(bbox, x) {
if (x < bbox.minX || x > bbox.maxX) {
return [];
}
return [{
x: x,
y: bbox.minY
}, {
x: x,
y: bbox.maxY
}];
};
var getBBoxYCrossPoints = exports.getBBoxYCrossPoints = function getBBoxYCrossPoints(bbox, y) {
if (y < bbox.minY || y > bbox.maxY) {
return [];
}
return [{
x: bbox.minX,
y: y
}, {
x: bbox.maxX,
y: y
}];
};
var getBBoxCrossPointsByPoint = exports.getBBoxCrossPointsByPoint = function getBBoxCrossPointsByPoint(bbox, point) {
return getBBoxXCrossPoints(bbox, point.x).concat(getBBoxYCrossPoints(bbox, point.y));
};
/**
* 曼哈顿距离
*/
var distance = exports.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
var _costByPoints = exports._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 的距离,减去其他路过节点造成的消耗
*/
var heuristicCostEstimate = exports.heuristicCostEstimate = function heuristicCostEstimate(p, ps, pt, source, target) {
return distance(p, ps) + distance(p, pt) + _costByPoints(p, [ps, pt, source, target]);
};
var _reconstructPath = exports.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
*/
var removeFrom = exports.removeFrom = function removeFrom(arr, item) {
var index = arr.indexOf(item);
if (index > -1) {
arr.splice(index, 1);
}
};
var isSegmentsIntersected = exports.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;
};
var isSegmentCrossingBBox = exports.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 的点
*/
var getNeighborPoints = exports.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 = exports.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;
}();
var pathFinder = exports.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];
};
var isBending = exports.isBending = function isBending(p0, p1, p2) {
return !(p0.x === p1.x && p1.x === p2.x || p0.y === p1.y && p1.y === p2.y);
};
var getBorderRadiusPoints = exports.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];
};
var getPathWithBorderRadiusByPolyline = exports.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('');
};
var getPolylinePoints = exports.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
*/
var removeRedundantPoint = exports.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;
};