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

385 lines
13 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.pathFinder = exports.octolinearCfg = void 0;
var _g6Core = require("@antv/g6-core");
var _util = require("@antv/util");
var _polylineUtil = require("./polyline-util");
/**
* 通过配置不同的 costFunc, distFunc, constraints 可以得到不同效果的 router
* generalRouter: 不限制搜索时的移动方向,避开障碍即可
* orthogonal: 线必须沿着竖直或水平方向4个方向
* octolinearRouter: 线沿着竖直、水平、对角线方向8个方向
*/
var manhattanDist = function manhattanDist(p1, p2) {
return Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
};
var eucliDist = function eucliDist(p1, p2) {
return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
};
var straightPath = function straightPath(start, end) {
// console.warn('fallbackRoute: straight path');
return [start, end];
};
var simplePolyline = function simplePolyline(start, end, startNode, endNode, cfg) {
return (0, _polylineUtil.simplifyPolyline)((0, _polylineUtil.getPolylinePoints)(start, end, startNode, endNode, cfg.offset));
};
// getPolylinePoints
var defaultCfg = {
offset: 20,
maxAllowedDirectionChange: Math.PI / 2,
maximumLoops: 2000,
gridSize: 10,
directions: [{
stepX: 1,
stepY: 0
}, {
stepX: -1,
stepY: 0
}, {
stepX: 0,
stepY: 1
}, {
stepX: 0,
stepY: -1
} // top
],
get penalties() {
return {
0: 0,
45: this.gridSize / 2,
90: this.gridSize / 2
};
},
distFunc: manhattanDist,
fallbackRoute: simplePolyline
};
var octolinearCfg = exports.octolinearCfg = {
maxAllowedDirectionChange: Math.PI / 4,
// 8 个方向: 上下左右 + 45度斜线方向
directions: [{
stepX: 1,
stepY: 0
}, {
stepX: 1,
stepY: 1
}, {
stepX: 0,
stepY: 1
}, {
stepX: -1,
stepY: 1
}, {
stepX: -1,
stepY: 0
}, {
stepX: -1,
stepY: -1
}, {
stepX: 0,
stepY: -1
}, {
stepX: 1,
stepY: -1
}],
distFunc: eucliDist,
fallbackRoute: straightPath
};
var pos2GridIx = function pos2GridIx(pos, gridSize) {
var gridIx = Math.round(Math.abs(pos / gridSize));
var sign = pos < 0 ? -1 : 1;
return gridIx < 0 ? 0 : sign * gridIx;
};
var getObstacleMap = function getObstacleMap(items, gridSize, offset) {
var map = {};
items.forEach(function (item) {
// create-edge 时,当边类型为 polyline 时 endNode 为 null
if (!item) return;
var bbox = (0, _polylineUtil.getExpandedBBox)(item.getBBox(), offset);
for (var x = pos2GridIx(bbox.minX, gridSize); x <= pos2GridIx(bbox.maxX, gridSize); x += 1) {
for (var y = pos2GridIx(bbox.minY, gridSize); y <= pos2GridIx(bbox.maxY, gridSize); y += 1) {
map["".concat(x, "|||").concat(y)] = true;
}
}
});
return map;
};
/**
* 方向角:计算从 p1 到 p2 的射线与水平线形成的夹角度数顺时针从右侧0°转到该射线的角度
* @param p1 PolyPoint
* @param p2 PolyPoint
*/
var getDirectionAngle = function getDirectionAngle(p1, p2) {
var deltaX = p2.x - p1.x;
var deltaY = p2.y - p1.y;
if (deltaX || deltaY) {
return Math.atan2(deltaY, deltaX);
}
return 0;
};
/**
* 方向角的改变取小于180度角
* @param angle1
* @param angle2
*/
var getAngleDiff = function getAngleDiff(angle1, angle2) {
var directionChange = Math.abs(angle1 - angle2);
return directionChange > Math.PI ? 2 * Math.PI - directionChange : directionChange;
// return directionChange > 180 ? 360 - directionChange : directionChange;
};
// Path finder //
var estimateCost = function estimateCost(from, endPoints, distFunc) {
var min = Infinity;
for (var i = 0, len = endPoints.length; i < len; i++) {
var cost = distFunc(from, endPoints[i]);
if (cost < min) {
min = cost;
}
}
return min;
};
// 计算考虑 offset 后的 BBox 上的连接点
var getBoxPoints = function getBoxPoints(point,
// 被 gridSize 格式化后的位置anchorPoint
oriPoint,
// 未被 gridSize 格式化的位置anchorPoint
node,
// 原始节点,用于获取 bbox
anotherPoint,
// 另一端被 gridSize 格式化后的位置
cfg) {
var points = [];
// create-edge 生成边的过程中endNode 为 null
if (!node) {
return [point];
}
var directions = cfg.directions,
offset = cfg.offset;
var bbox = node.getBBox();
var isInside = oriPoint.x > bbox.minX && oriPoint.x < bbox.maxX && oriPoint.y > bbox.minY && oriPoint.y < bbox.maxY;
var expandBBox = (0, _polylineUtil.getExpandedBBox)(bbox, offset);
for (var i in expandBBox) {
expandBBox[i] = pos2GridIx(expandBBox[i], cfg.gridSize);
}
if (isInside) {
// 如果 anchorPoint 在节点内部,允许第一段线穿过节点
for (var _i = 0, directions_1 = directions; _i < directions_1.length; _i++) {
var dir = directions_1[_i];
var bounds = [[{
x: expandBBox.minX,
y: expandBBox.minY
}, {
x: expandBBox.maxX,
y: expandBBox.minY
}], [{
x: expandBBox.minX,
y: expandBBox.minY
}, {
x: expandBBox.minX,
y: expandBBox.maxY
}], [{
x: expandBBox.maxX,
y: expandBBox.minY
}, {
x: expandBBox.maxX,
y: expandBBox.maxY
}], [{
x: expandBBox.minX,
y: expandBBox.maxY
}, {
x: expandBBox.maxX,
y: expandBBox.maxY
}]];
for (var i = 0; i < 4; i++) {
var boundLine = bounds[i];
var insterctP_1 = _g6Core.Util.getLineIntersect(point, {
x: point.x + dir.stepX * expandBBox.width,
y: point.y + dir.stepY * expandBBox.height
}, boundLine[0], boundLine[1]);
if (insterctP_1 && !(0, _polylineUtil.isSegmentCrossingBBox)(point, insterctP_1, bbox)) {
insterctP_1.id = "".concat(insterctP_1.x, "|||").concat(insterctP_1.y);
points.push(insterctP_1);
}
}
}
return points;
}
// 如果 anchorPoint 在节点上,只有一个可选方向
var insterctP = (0, _polylineUtil.getExpandedBBoxPoint)(expandBBox, point, anotherPoint);
insterctP.id = "".concat(insterctP.x, "|||").concat(insterctP.y);
return [insterctP];
};
var getDirectionChange = function getDirectionChange(current, neighbor, cameFrom, scaleStartPoint) {
var directionAngle = getDirectionAngle(current, neighbor);
var currentCameFrom = cameFrom[current.id];
if (!currentCameFrom) {
var startAngle = getDirectionAngle(scaleStartPoint, current);
return getAngleDiff(startAngle, directionAngle);
}
var prevDirectionAngle = getDirectionAngle({
x: currentCameFrom.x,
y: currentCameFrom.y
}, current);
return getAngleDiff(prevDirectionAngle, directionAngle);
};
var getControlPoints = function getControlPoints(current, cameFrom, scaleStartPoint, endPoint, startPoint, scaleEndPoint, gridSize) {
var controlPoints = [endPoint];
var pointZero = endPoint;
var currentId = current.id;
var currentX = current.x;
var currentY = current.y;
var lastPoint = {
x: currentX,
y: currentY,
id: currentId
};
if (getDirectionChange(lastPoint, scaleEndPoint, cameFrom, scaleStartPoint)) {
pointZero = {
x: scaleEndPoint.x === endPoint.x ? endPoint.x : lastPoint.x * gridSize,
y: scaleEndPoint.y === endPoint.y ? endPoint.y : lastPoint.y * gridSize
};
controlPoints.unshift(pointZero);
}
var currentCameFrom = cameFrom[currentId];
while (currentCameFrom && currentCameFrom.id !== currentId) {
var point = {
x: currentX,
y: currentY,
id: currentId
};
var prePoint = {
x: currentCameFrom.x,
y: currentCameFrom.y,
id: currentCameFrom.id
};
var directionChange = getDirectionChange(prePoint, point, cameFrom, scaleStartPoint);
if (directionChange) {
pointZero = {
x: prePoint.x === point.x ? pointZero.x : prePoint.x * gridSize,
y: prePoint.y === point.y ? pointZero.y : prePoint.y * gridSize
};
controlPoints.unshift(pointZero);
}
currentId = prePoint.id;
currentX = prePoint.x;
currentY = prePoint.y;
currentCameFrom = cameFrom[currentId];
}
// 和startNode对齐
controlPoints[0].x = currentX === scaleStartPoint.x ? startPoint.x : pointZero.x;
controlPoints[0].y = currentY === scaleStartPoint.y ? startPoint.y : pointZero.y;
controlPoints.unshift(startPoint);
return controlPoints;
};
var pathFinder = exports.pathFinder = function pathFinder(startPoint, endPoint, startNode, endNode, routerCfg) {
if (isNaN(startPoint.x) || isNaN(endPoint.x)) return [];
var cfg = (0, _util.deepMix)(defaultCfg, routerCfg);
cfg.obstacles = cfg.obstacles || [];
var penalties = cfg.penalties,
gridSize = cfg.gridSize;
var map = getObstacleMap(cfg.obstacles.concat([startNode, endNode]), gridSize, cfg.offset);
var scaleStartPoint = {
x: pos2GridIx(startPoint.x, gridSize),
y: pos2GridIx(startPoint.y, gridSize)
};
var scaleEndPoint = {
x: pos2GridIx(endPoint.x, gridSize),
y: pos2GridIx(endPoint.y, gridSize)
};
startPoint.id = "".concat(scaleStartPoint.x, "|||").concat(scaleStartPoint.y);
endPoint.id = "".concat(scaleEndPoint.x, "|||").concat(scaleEndPoint.y);
var startPoints = getBoxPoints(scaleStartPoint, startPoint, startNode, scaleEndPoint, cfg);
var endPoints = getBoxPoints(scaleEndPoint, endPoint, endNode, scaleStartPoint, cfg);
startPoints.forEach(function (point) {
delete map[point.id];
});
endPoints.forEach(function (point) {
delete map[point.id];
});
var openSet = {};
var closedSet = {};
var cameFrom = {};
// 从起点到当前点已产生的 cost, default: Infinity
var gScore = {};
// 起点经过当前点到达终点预估的 cost, default: Infinity
var fScore = {};
var sortedOpenSet = new _polylineUtil.SortedArray();
// initialize
for (var i = 0; i < startPoints.length; i++) {
var firstStep = startPoints[i];
openSet[firstStep.id] = firstStep;
gScore[firstStep.id] = 0;
fScore[firstStep.id] = estimateCost(firstStep, endPoints, cfg.distFunc);
sortedOpenSet.add({
id: firstStep.id,
value: fScore[firstStep.id]
});
}
var remainLoops = cfg.maximumLoops;
var current, direction, neighbor, neighborCost, costFromStart, directionChange;
var curCost = Infinity;
var endPointMap = {};
endPoints.forEach(function (point) {
endPointMap["".concat(point.x, "|||").concat(point.y)] = true;
});
Object.keys(openSet).forEach(function (key) {
var id = openSet[key].id;
if (fScore[id] <= curCost) {
curCost = fScore[id];
current = openSet[id];
}
});
while (Object.keys(openSet).length > 0 && remainLoops > 0) {
var minId = sortedOpenSet.minId((remainLoops + 1) % 30 === 0);
if (minId) {
current = openSet[minId];
} else {
break;
}
// 如果 fScore 最小的点就是终点
if (endPointMap["".concat(current.x, "|||").concat(current.y)]) {
return getControlPoints(current, cameFrom, scaleStartPoint, endPoint, startPoint, scaleEndPoint, gridSize);
}
delete openSet[current.id];
sortedOpenSet.remove(current.id);
closedSet[current.id] = true;
// 获取符合条件的下一步的候选连接点
// 沿候选方向走一步
for (var i = 0; i < cfg.directions.length; i++) {
direction = cfg.directions[i];
var neighborId = "".concat(Math.round(current.x) + direction.stepX, "|||").concat(Math.round(current.y) + direction.stepY);
neighbor = {
x: current.x + direction.stepX,
y: current.y + direction.stepY,
id: neighborId
};
if (closedSet[neighborId]) continue;
directionChange = getDirectionChange(current, neighbor, cameFrom, scaleStartPoint);
if (directionChange > cfg.maxAllowedDirectionChange) continue;
if (map[neighborId]) continue; // 如果交叉则跳过
// 将候选点加入 openSet, 并计算每个候选点的 cost
if (!openSet[neighborId]) {
openSet[neighborId] = neighbor;
}
var directionPenalties = penalties[directionChange];
neighborCost = cfg.distFunc(current, neighbor) + (isNaN(directionPenalties) ? gridSize : directionPenalties);
costFromStart = gScore[current.id] + neighborCost;
var neighborGScore = gScore[neighborId];
if (neighborGScore && costFromStart >= neighborGScore) {
continue;
}
cameFrom[neighborId] = current;
gScore[neighborId] = costFromStart;
fScore[neighborId] = costFromStart + estimateCost(neighbor, endPoints, cfg.distFunc);
sortedOpenSet.add({
id: neighborId,
value: fScore[neighborId]
});
}
remainLoops -= 1;
}
return cfg.fallbackRoute(startPoint, endPoint, startNode, endNode, cfg);
};