You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
4.5 KiB
149 lines
4.5 KiB
|
4 months ago
|
import { isNumber } from '@antv/util';
|
||
|
|
import { isArray } from './array';
|
||
|
|
export const floydWarshall = (adjMatrix) => {
|
||
|
|
// initialize
|
||
|
|
const dist = [];
|
||
|
|
const size = adjMatrix.length;
|
||
|
|
for (let i = 0; i < size; i += 1) {
|
||
|
|
dist[i] = [];
|
||
|
|
for (let j = 0; j < size; j += 1) {
|
||
|
|
if (i === j) {
|
||
|
|
dist[i][j] = 0;
|
||
|
|
}
|
||
|
|
else if (adjMatrix[i][j] === 0 || !adjMatrix[i][j]) {
|
||
|
|
dist[i][j] = Infinity;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
dist[i][j] = adjMatrix[i][j];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// floyd
|
||
|
|
for (let k = 0; k < size; k += 1) {
|
||
|
|
for (let i = 0; i < size; i += 1) {
|
||
|
|
for (let j = 0; j < size; j += 1) {
|
||
|
|
if (dist[i][j] > dist[i][k] + dist[k][j]) {
|
||
|
|
dist[i][j] = dist[i][k] + dist[k][j];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return dist;
|
||
|
|
};
|
||
|
|
export const getAdjMatrix = (data, directed) => {
|
||
|
|
const { nodes, edges } = data;
|
||
|
|
const matrix = [];
|
||
|
|
// map node with index in data.nodes
|
||
|
|
const nodeMap = {};
|
||
|
|
if (!nodes) {
|
||
|
|
throw new Error('invalid nodes data!');
|
||
|
|
}
|
||
|
|
if (nodes) {
|
||
|
|
nodes.forEach((node, i) => {
|
||
|
|
nodeMap[node.id] = i;
|
||
|
|
const row = [];
|
||
|
|
matrix.push(row);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
edges === null || edges === void 0 ? void 0 : edges.forEach((e) => {
|
||
|
|
const { source, target } = e;
|
||
|
|
const sIndex = nodeMap[source];
|
||
|
|
const tIndex = nodeMap[target];
|
||
|
|
if (sIndex === undefined || tIndex === undefined)
|
||
|
|
return;
|
||
|
|
matrix[sIndex][tIndex] = 1;
|
||
|
|
if (!directed) {
|
||
|
|
matrix[tIndex][sIndex] = 1;
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return matrix;
|
||
|
|
};
|
||
|
|
/**
|
||
|
|
* scale matrix
|
||
|
|
* @param matrix [ [], [], [] ]
|
||
|
|
* @param ratio
|
||
|
|
*/
|
||
|
|
export const scaleMatrix = (matrix, ratio) => {
|
||
|
|
const result = [];
|
||
|
|
matrix.forEach((row) => {
|
||
|
|
const newRow = [];
|
||
|
|
row.forEach((v) => {
|
||
|
|
newRow.push(v * ratio);
|
||
|
|
});
|
||
|
|
result.push(newRow);
|
||
|
|
});
|
||
|
|
return result;
|
||
|
|
};
|
||
|
|
/**
|
||
|
|
* calculate the bounding box for the nodes according to their x, y, and size
|
||
|
|
* @param nodes nodes in the layout
|
||
|
|
* @returns
|
||
|
|
*/
|
||
|
|
export const getLayoutBBox = (nodes) => {
|
||
|
|
let minX = Infinity;
|
||
|
|
let minY = Infinity;
|
||
|
|
let maxX = -Infinity;
|
||
|
|
let maxY = -Infinity;
|
||
|
|
nodes.forEach((node) => {
|
||
|
|
let size = node.data.size;
|
||
|
|
if (isArray(size)) {
|
||
|
|
if (size.length === 1)
|
||
|
|
size = [size[0], size[0]];
|
||
|
|
}
|
||
|
|
else if (isNumber(size)) {
|
||
|
|
size = [size, size];
|
||
|
|
}
|
||
|
|
else if (size === undefined || isNaN(size)) {
|
||
|
|
size = [30, 30];
|
||
|
|
}
|
||
|
|
const halfSize = [size[0] / 2, size[1] / 2];
|
||
|
|
const left = node.data.x - halfSize[0];
|
||
|
|
const right = node.data.x + halfSize[0];
|
||
|
|
const top = node.data.y - halfSize[1];
|
||
|
|
const bottom = node.data.y + halfSize[1];
|
||
|
|
if (minX > left)
|
||
|
|
minX = left;
|
||
|
|
if (minY > top)
|
||
|
|
minY = top;
|
||
|
|
if (maxX < right)
|
||
|
|
maxX = right;
|
||
|
|
if (maxY < bottom)
|
||
|
|
maxY = bottom;
|
||
|
|
});
|
||
|
|
return { minX, minY, maxX, maxY };
|
||
|
|
};
|
||
|
|
/**
|
||
|
|
* calculate the euclidean distance form p1 to p2
|
||
|
|
* @param p1
|
||
|
|
* @param p2
|
||
|
|
* @returns
|
||
|
|
*/
|
||
|
|
export const getEuclideanDistance = (p1, p2) => Math.sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
|
||
|
|
/**
|
||
|
|
* Depth first search begin from nodes in graphCore data.
|
||
|
|
* @param graphCore graphlib data structure
|
||
|
|
* @param nodes begin nodes
|
||
|
|
* @param fn will be called while visiting each node
|
||
|
|
* @param mode 'TB' - visit from top to bottom; 'BT' - visit from bottom to top;
|
||
|
|
* @returns
|
||
|
|
*/
|
||
|
|
export const graphTreeDfs = (graph, nodes, fn, mode = 'TB', treeKey, stopFns = {}) => {
|
||
|
|
if (!(nodes === null || nodes === void 0 ? void 0 : nodes.length))
|
||
|
|
return;
|
||
|
|
const { stopBranchFn, stopAllFn } = stopFns;
|
||
|
|
for (let i = 0; i < nodes.length; i++) {
|
||
|
|
const node = nodes[i];
|
||
|
|
if (!graph.hasNode(node.id))
|
||
|
|
continue;
|
||
|
|
if (stopBranchFn === null || stopBranchFn === void 0 ? void 0 : stopBranchFn(node))
|
||
|
|
continue; // Stop this branch
|
||
|
|
if (stopAllFn === null || stopAllFn === void 0 ? void 0 : stopAllFn(node))
|
||
|
|
return; // Stop all
|
||
|
|
if (mode === 'TB')
|
||
|
|
fn(node); // Traverse from top to bottom
|
||
|
|
graphTreeDfs(graph, graph.getChildren(node.id, treeKey), fn, mode, treeKey, stopFns);
|
||
|
|
if (mode !== 'TB')
|
||
|
|
fn(node); // Traverse from bottom to top
|
||
|
|
}
|
||
|
|
};
|
||
|
|
//# sourceMappingURL=math.js.map
|