@antv/g6
Version:
graph visualization frame work
338 lines (303 loc) • 8.62 kB
JavaScript
/**
* @fileOverview math util
* @author huangtonger@aliyun.com
*/
var BaseUtil = require('./base');
var tolerance = 0.001;
var MathUtil = {
/**
* 是否在区间内
* @param {number} value 值
* @param {number} min 最小值
* @param {number} max 最大值
* @return {boolean} bool 布尔
*/
isBetween: function isBetween(value, min, max) {
return value >= min && value <= max;
},
/**
* 两线段交点
* @param {object} p0 第一条线段起点
* @param {object} p1 第一条线段终点
* @param {object} p2 第二条线段起点
* @param {object} p3 第二条线段终点
* @return {object} 交点
*/
getLineIntersect: function getLineIntersect(p0, p1, p2, p3) {
var E = {
x: p2.x - p0.x,
y: p2.y - p0.y
};
var D0 = {
x: p1.x - p0.x,
y: p1.y - p0.y
};
var D1 = {
x: p3.x - p2.x,
y: p3.y - p2.y
};
var kross = D0.x * D1.y - D0.y * D1.x;
var sqrKross = kross * kross;
var sqrLen0 = D0.x * D0.x + D0.y * D0.y;
var sqrLen1 = D1.x * D1.x + D1.y * D1.y;
var point = null;
if (sqrKross > tolerance * sqrLen0 * sqrLen1) {
var s = (E.x * D1.y - E.y * D1.x) / kross;
var t = (E.x * D0.y - E.y * D0.x) / kross;
if (MathUtil.isBetween(s, 0, 1) && MathUtil.isBetween(t, 0, 1)) {
point = {
x: p0.x + s * D0.x,
y: p0.y + s * D0.y
};
}
}
return point;
},
/**
* point and rectangular intersection point
* @param {object} rect rect
* @param {object} point point
* @return {object} rst;
*/
getRectIntersectByPoint: function getRectIntersectByPoint(rect, point) {
var x = rect.x,
y = rect.y,
width = rect.width,
height = rect.height;
var cx = x + width / 2;
var cy = y + height / 2;
var points = [];
var center = {
x: cx,
y: cy
};
points.push({
x: x,
y: y
});
points.push({
x: x + width,
y: y
});
points.push({
x: x + width,
y: y + height
});
points.push({
x: x,
y: y + height
});
points.push({
x: x,
y: y
});
var rst = null;
for (var i = 1; i < points.length; i++) {
rst = MathUtil.getLineIntersect(points[i - 1], points[i], center, point);
if (rst) {
break;
}
}
return rst;
},
/**
* get point and circle inIntersect
* @param {Object} circle 圆点,x,y,r
* @param {Object} point 点 x,y
* @return {object} applied point
*/
getCircleIntersectByPoint: function getCircleIntersectByPoint(circle, point) {
var cx = circle.x;
var cy = circle.y;
var r = circle.r;
var x = point.x,
y = point.y;
var d = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2));
if (d < r) {
return null;
}
var dx = x - cx;
var dy = y - cy;
var signX = Math.sign(dx);
var signY = Math.sign(dy);
var angle = Math.atan(dy / dx);
return {
x: cx + Math.abs(r * Math.cos(angle)) * signX,
y: cy + Math.abs(r * Math.sin(angle)) * signY
};
},
/**
* get point and ellipse inIntersect
* @param {Object} ellipse 椭圆 x,y,rx,ry
* @param {Object} point 点 x,y
* @return {object} applied point
*/
getEllispeIntersectByPoint: function getEllispeIntersectByPoint(ellipse, point) {
// 计算线段 (point.x, point.y) 到 (ellipse.x, ellipse.y) 与椭圆的交点
var a = ellipse.rx;
var b = ellipse.ry;
var cx = ellipse.x;
var cy = ellipse.y; // const c = Math.sqrt(a * a - b * b); // 焦距
var dx = point.x - cx;
var dy = point.y - cy;
var angle = Math.atan2(dy / b, dx / a); // 直接通过 x,y 求夹角,求出来的范围是 -PI, PI
if (angle < 0) {
angle += 2 * Math.PI; // 转换到 0,2PI
} // 通过参数方程求交点
// const r = (b * b) / (a - c * Math.sin(angle));
return {
x: cx + a * Math.cos(angle),
y: cy + b * Math.sin(angle)
};
},
/**
* coordinate matrix transformation
* @param {number} point coordinate
* @param {number} matrix matrix
* @param {number} tag could be 0 or 1
* @return {object} transformed point
*/
applyMatrix: function applyMatrix(point, matrix, tag) {
if (tag === void 0) {
tag = 1;
}
var vector = [point.x, point.y, tag];
BaseUtil.vec3.transformMat3(vector, vector, matrix);
return {
x: vector[0],
y: vector[1]
};
},
/**
* coordinate matrix invert transformation
* @param {number} point coordinate
* @param {number} matrix matrix
* @param {number} tag could be 0 or 1
* @return {object} transformed point
*/
invertMatrix: function invertMatrix(point, matrix, tag) {
if (tag === void 0) {
tag = 1;
}
var inversedMatrix = BaseUtil.mat3.invert([], matrix);
var vector = [point.x, point.y, tag];
BaseUtil.vec3.transformMat3(vector, vector, inversedMatrix);
return {
x: vector[0],
y: vector[1]
};
},
/**
* Floyd Warshall algorithm for shortest path distances matrix
* @param {array} adjMatrix adjacency matrix
* @return {array} distances shortest path distances matrix
*/
floydWarshall: function floydWarshall(adjMatrix) {
// initialize
var dist = [];
var size = adjMatrix.length;
for (var i = 0; i < size; i += 1) {
dist[i] = [];
for (var 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 (var k = 0; k < size; k += 1) {
for (var _i = 0; _i < size; _i += 1) {
for (var _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;
},
getAdjMatrix: function getAdjMatrix(data, directed) {
var nodes = data.nodes;
var edges = data.edges;
var matrix = []; // map node with index in data.nodes
var nodeMap = new Map();
nodes.forEach(function (node, i) {
nodeMap.set(node.id, i);
var row = [];
matrix.push(row);
}); // const n = nodes.length;
edges.forEach(function (e) {
var source = e.source;
var target = e.target;
var sIndex = nodeMap.get(source);
var tIndex = nodeMap.get(target);
matrix[sIndex][tIndex] = 1;
if (!directed) matrix[tIndex][sIndex] = 1;
});
return matrix;
},
/**
* if the graph about the shortest path matrix is connected
* @param {array} matrix shortest path matrix
* @return {boolean} connected
*/
isConnected: function isConnected(matrix) {
if (matrix.length > 0) {
for (var j = 0; j < matrix[0].length; j++) {
if (matrix[0][j] === Infinity) return false;
}
}
return true;
},
getEDistance: function getEDistance(p1, p2) {
return Math.sqrt((p1[0] - p2[0]) * (p1[0] - p2[0]) + (p1[1] - p2[1]) * (p1[1] - p2[1]));
},
scaleMatrix: function scaleMatrix(matrix, scale) {
var result = [];
matrix.forEach(function (row) {
var newRow = [];
row.forEach(function (v) {
newRow.push(v * scale);
});
result.push(newRow);
});
return result;
},
randomInitPos: function randomInitPos(size, xRange, yRange) {
if (xRange === void 0) {
xRange = [0, 1];
}
if (yRange === void 0) {
yRange = [0, 1];
}
var positions = [];
for (var i = 0; i < size; i++) {
var x = Math.random() * (xRange[1] - xRange[0]) + xRange[0];
var y = Math.random() * (yRange[1] - yRange[0]) + yRange[0];
positions.push([x, y]);
}
return positions;
},
getCircleCenterByPoints: function getCircleCenterByPoints(p1, p2, p3) {
var a = p1.x - p2.x;
var b = p1.y - p2.y;
var c = p1.x - p3.x;
var d = p1.y - p3.y;
var e = (p1.x * p1.x - p2.x * p2.x - p2.y * p2.y + p1.y * p1.y) / 2;
var f = (p1.x * p1.x - p3.x * p3.x - p3.y * p3.y + p1.y * p1.y) / 2;
var denominator = b * c - a * d;
return {
x: -(d * e - b * f) / denominator,
y: -(a * f - c * e) / denominator
};
},
distance: function distance(p1, p2) {
var vx = p1.x - p2.x;
var vy = p1.y - p2.y;
return Math.sqrt(vx * vx + vy * vy);
}
};
module.exports = BaseUtil.mix({}, BaseUtil, MathUtil);