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

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