@antv/g6
Version:
graph visualization frame work
423 lines (389 loc) • 12.3 kB
JavaScript
var Util = require('../../util/index');
export var getBBoxFromPoint = function getBBoxFromPoint(point) {
var x = point.x,
y = point.y;
return {
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 pointsMap = {};
points.forEach(function (p) {
var id = p.id = p.x + "-" + p.y;
pointsMap[id] = p;
});
Util.each(pointsMap, function (p) {
result.push(p);
});
return result;
};
export var simplifyPolyline = function simplifyPolyline(points) {
points = filterConnectPoints(points);
return 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 === 0 && bbox.height === 0) {
// when it is a point
return bbox;
}
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
};
};
export var isHorizontalPort = function isHorizontalPort(port, bbox) {
var dx = Math.abs(port.x - bbox.centerX);
var dy = Math.abs(port.y - bbox.centerY);
return dx / bbox.width > dy / bbox.height;
};
export var getExpandedBBoxPoint = function getExpandedBBoxPoint(bbox, point) {
var isHorizontal = isHorizontalPort(point, bbox);
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 {object} b1 bbox1
* @param {object} b2 bbox2
* @return {object} { centerX, centerY, height, maxX, maxY, minX, minY, width }
**/
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
var minX = bbox.minX,
minY = bbox.minY,
maxX = bbox.maxX,
maxY = bbox.maxY;
return [{
x: minX,
y: minY
}, {
x: maxX,
y: minY
}, {
x: maxX,
y: maxY
}, {
x: minX,
y: 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);
};
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;
};
export var heuristicCostEstimate = function heuristicCostEstimate(p, ps, pt, source, target) {
return distance(p, ps) + distance(p, pt) + _costByPoints(p, [ps, pt, source, target]);
};
export 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);
}
};
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 s1_x = p1.x - p0.x;
var s1_y = p1.y - p0.y;
var s2_x = p3.x - p2.x;
var s2_y = p3.y - p2.y;
var s = (-s1_y * (p0.x - p2.x) + s1_x * (p0.y - p2.y)) / (-s2_x * s1_y + s1_x * s2_y);
var t = (s2_x * (p0.y - p2.y) - s2_y * (p0.x - p2.x)) / (-s2_x * s1_y + s1_x * s2_y);
return s >= 0 && s <= 1 && t >= 0 && t <= 1;
};
export var isSegmentCrossingBBox = function isSegmentCrossingBBox(p1, p2, bbox) {
if (bbox.width === bbox.height === 0) {
return false;
}
var _getPointsFromBBox = getPointsFromBBox(bbox),
pa = _getPointsFromBBox[0],
pb = _getPointsFromBBox[1],
pc = _getPointsFromBBox[2],
pd = _getPointsFromBBox[3];
return isSegmentsIntersected(p1, p2, pa, pb) || isSegmentsIntersected(p1, p2, pa, pd) || isSegmentsIntersected(p1, p2, pb, pc) || isSegmentsIntersected(p1, p2, pc, pd);
};
export var getNeighborPoints = function getNeighborPoints(points, point, bbox1, bbox2) {
var neighbors = [];
points.forEach(function (p) {
if (p !== point) {
if (p.x === point.x || p.y === point.y) {
if (!isSegmentCrossingBBox(p, point, bbox1) && !isSegmentCrossingBBox(p, point, bbox2)) {
neighbors.push(p);
}
}
}
});
return filterConnectPoints(neighbors);
};
export var pathFinder = function pathFinder(points, start, goal, sBBox, tBBox, os, ot) {
// A-Star Algorithm
var closedSet = [];
var openSet = [start];
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 pointById = {};
points.forEach(function (p) {
pointById[p.id] = p;
});
var _loop = function _loop() {
var current = void 0;
var lowestFScore = Infinity;
openSet.forEach(function (p) {
if (fScore[p.id] < lowestFScore) {
lowestFScore = fScore[p.id];
current = p;
}
});
if (current === goal) {
// ending condition
var pathPoints = [];
reconstructPath(pathPoints, pointById, cameFrom, goal.id);
return {
v: pathPoints
};
}
removeFrom(openSet, current);
closedSet.push(current);
getNeighborPoints(points, current, sBBox, tBBox).forEach(function (neighbor) {
if (closedSet.indexOf(neighbor) !== -1) return;
if (openSet.indexOf(neighbor) === -1) {
openSet.push(neighbor);
}
var tentativeGScore = fScore[current.id] + distance(current, neighbor); // + distance(neighbor, goal);
if (gScore[neighbor.id] && tentativeGScore >= gScore[neighbor.id]) return;
cameFrom[neighbor.id] = current.id;
gScore[neighbor.id] = tentativeGScore;
fScore[neighbor.id] = gScore[neighbor.id] + heuristicCostEstimate(neighbor, goal, start, os, ot);
});
};
while (openSet.length) {
var _ret = _loop();
if (typeof _ret === "object") return _ret.v;
} // throw new Error('Cannot find path');
return [start, goal];
};
export var isBending = function isBending(p0, p1, p2) {
return !(p0.x === p1.x === p2.x || p0.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) {
// TODO
var pathSegments = [];
var startPoint = points[0];
pathSegments.push("M" + startPoint.x + " " + 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 _getBorderRadiusPoint = getBorderRadiusPoints(p, p1, p2, borderRadius),
ps = _getBorderRadiusPoint[0],
pt = _getBorderRadiusPoint[1];
pathSegments.push("L" + ps.x + " " + ps.y);
pathSegments.push("Q" + p1.x + " " + p1.y + " " + pt.x + " " + pt.y);
pathSegments.push("L" + pt.x + " " + pt.y);
} else {
pathSegments.push("L" + p1.x + " " + p1.y);
}
} else if (p1) {
pathSegments.push("L" + p1.x + " " + p1.y);
}
});
return pathSegments.join('');
};
export var getPolylinePoints = function getPolylinePoints(start, end, sNode, tNode, offset) {
var sBBox = sNode && sNode.getBBox() ? sNode.getBBox() : getBBoxFromPoint(start);
var tBBox = tNode && tNode.getBBox() ? tNode.getBBox() : getBBoxFromPoint(end);
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);
var tPoint = getExpandedBBoxPoint(txBBox, end);
var lineBBox = getBBoxFromPoints([sPoint, tPoint]);
var outerBBox = mergeBBox(sxBBox, txBBox);
var sMixBBox = mergeBBox(sxBBox, lineBBox);
var tMixBBox = mergeBBox(txBBox, lineBBox);
var connectPoints = [];
connectPoints = connectPoints.concat(getPointsFromBBox(sMixBBox) // .filter(p => !isPointIntersectBBox(p, txBBox))
);
connectPoints = connectPoints.concat(getPointsFromBBox(tMixBBox) // .filter(p => !isPointIntersectBBox(p, sxBBox))
);
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) {
if (isPointOutsideBBox(p, sxBBox) && isPointOutsideBBox(p, txBBox) // &&
// isPointInsideBBox(p, sMixBBox) && isPointInsideBBox(p, tMixBBox)
) {
connectPoints.push(p);
}
});
connectPoints.unshift(sPoint);
connectPoints.push(tPoint);
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);
};