butterfly-dag
Version:
一个基于数据驱动的节点式编排组件库,让你有方便快捷定制可视化流程图表
411 lines (381 loc) • 12.2 kB
JavaScript
'use strict';
import _ from 'lodash';
const MINDIST = 20;
const TOL = 0.1;
const TOLxTOL = 0.01;
const TOGGLE_DIST = 20;
// const Point = function(x, y) {
// this.x = x;
// this.y = y;
// }
export const LEFT = 'Left';
export const RIGHT = 'Right';
export const TOP = 'Top';
export const BOTTOM = 'Bottom';
// 曼哈顿折线路由算法
export function _route(conn, fromPt, fromDir, toPt, toDir) {
// 防止图上节点隐藏NaN的死循环问题
fromPt.x = fromPt.x || 0;
fromPt.y = fromPt.y || 0;
toPt.x = toPt.x || 0;
toPt.y = toPt.y || 0;
const xDiff = fromPt.x - toPt.x;
const yDiff = fromPt.y - toPt.y;
let point;
let dir;
let pos;
conn.push({x: fromPt.x, y: fromPt.y});
if (((xDiff * xDiff) < (TOLxTOL)) && ((yDiff * yDiff) < (TOLxTOL))) {
// conn.push({x: toPt.x, y: toPt.y});
return;
}
if (fromDir === LEFT) {
if ((xDiff > 0) && ((yDiff * yDiff) < TOL) && (toDir === RIGHT)) {
point = toPt
dir = toDir
}
else {
if (xDiff < 0) {
point = {x: fromPt.x - MINDIST, y: fromPt.y};
}
else if (((yDiff > 0) && (toDir === BOTTOM)) || ((yDiff < 0) && (toDir === TOP))) {
point = {x: toPt.x, y: fromPt.y};
}
else if (fromDir === toDir) {
pos = Math.min(fromPt.x, toPt.x) - MINDIST
point = {x: pos, y: fromPt.y};
}
else {
point = {x: fromPt.x - (xDiff / 2), y: fromPt.y};
}
if (yDiff > 0) {
dir = TOP
}
else {
dir = BOTTOM
}
}
} else if (fromDir === RIGHT) {
if ((xDiff < 0) && ((yDiff * yDiff) < TOL) && (toDir === LEFT)) {
point = toPt
dir = toDir
}
else {
if (xDiff > 0) {
point = {x: fromPt.x + MINDIST, y: fromPt.y};
}
else if (((yDiff > 0) && (toDir === BOTTOM)) || ((yDiff < 0) && (toDir === TOP))) {
point = {x: toPt.x, y: fromPt.y};
}
else if (fromDir === toDir) {
pos = Math.max(fromPt.x, toPt.x) + MINDIST
point = {x: pos, y: fromPt.y};
}
else {
point = {x: fromPt.x - (xDiff / 2), y: fromPt.y};
}
if (yDiff > 0) {
dir = TOP;
}
else {
dir = BOTTOM;
}
}
} else if (fromDir === BOTTOM) {
if (((xDiff * xDiff) < TOL) && (yDiff < 0) && (toDir === TOP)) {
point = toPt;
dir = toDir;
}
else {
if (yDiff > 0) {
point = {x: fromPt.x, y: fromPt.y + MINDIST};
}
else if (((xDiff > 0) && (toDir === RIGHT)) || ((xDiff < 0) && (toDir === LEFT))) {
point = {x: fromPt.x, y: toPt.y};
}
else if (fromDir === toDir) {
pos = Math.max(fromPt.y, toPt.y) + MINDIST;
point = {x: fromPt.x, y: pos};
}
else {
point = {x: fromPt.x, y: fromPt.y - (yDiff / 2)};
}
if (xDiff > 0) {
dir = LEFT;
}
else {
dir = RIGHT;
}
}
} else if (fromDir === TOP) {
if (((xDiff * xDiff) < TOL) && (yDiff > 0) && (toDir === BOTTOM)) {
point = toPt;
dir = toDir;
}
else {
if (yDiff < 0) {
point = {x: fromPt.x, y: fromPt.y - MINDIST};
}
else if (((xDiff > 0) && (toDir === RIGHT)) || ((xDiff < 0) && (toDir === LEFT))) {
point = {x: fromPt.x, y: toPt.y};
}
else if (fromDir === toDir) {
pos = Math.min(fromPt.y, toPt.y) - MINDIST
point = {x: fromPt.x, y: pos};
}
else {
point = {x: fromPt.x, y: fromPt.y - (yDiff / 2)};
}
if (xDiff > 0) {
dir = LEFT;
}
else {
dir = RIGHT;
}
}
}
_route(conn, point, dir, toPt, toDir);
}
export function _calcOrientation(beginX, beginY, endX, endY, orientationLimit) {
let _calcWithLimit = (rank) => {
if (orientationLimit) {
for (let i = 0; i < rank.length; i++) {
let isInLimit = _.some(orientationLimit, (limit) => {
return limit === rank[i];
});
if (isInLimit) {
return rank[i];
}
}
return rank[0];
} else {
return rank[0];
}
};
// 计算orientation
let posX = endX - beginX;
let posY = endY - beginY;
let orientation = null;
// 斜率
let k = Math.abs(posY / posX);
if (posX === 0 || posY === 0) {
if (posX === 0) {
orientation = posY >= 0 ? _calcWithLimit(['Top', 'Left', 'Right', 'Bottom']) : orientation;
orientation = posY < 0 ? _calcWithLimit(['Bottom', 'Left', 'Right', 'Top']) : orientation;
}
if (posY === 0) {
orientation = posX >= 0 ? _calcWithLimit(['Right', 'Top', 'Bottom', 'Left']) : orientation;
orientation = posX < 0 ? _calcWithLimit(['Left', 'Top', 'Bottom', 'Right']) : orientation;
}
} else if (posX > 0 && posY > 0) {
if (k > 1) {
orientation = _calcWithLimit(['Top', 'Left', 'Right', 'Bottom']);
// orientation = [0, -1];
} else {
orientation = _calcWithLimit(['Left', 'Top', 'Bottom', 'Right']);
// orientation = [-1, 0];
}
} else if (posX < 0 && posY > 0) {
if (k > 1) {
orientation = _calcWithLimit(['Top', 'Right', 'Left', 'Bottom']);
// orientation = [0, -1];
} else {
orientation = _calcWithLimit(['Right', 'Top', 'Bottom', 'Left']);
// orientation = [1, 0];
}
} else if (posX < 0 && posY < 0) {
if (k > 1) {
orientation = _calcWithLimit(['Bottom', 'Right', 'Left', 'Top']);
// orientation = [0, 1];
} else {
orientation = _calcWithLimit(['Right', 'Bottom', 'Top', 'Left']);
// orientation = [1, 0];
}
} else {
if (k > 1) {
orientation = _calcWithLimit(['Bottom', 'Left', 'Right', 'Top']);
// orientation = [0, 1];
} else {
orientation = _calcWithLimit(['Left', 'Bottom', 'Top', 'Right']);
// orientation = [-1, 0];
}
}
switch (orientation) {
case 'Left':
return [-1, 0];
case 'Right':
return [1, 0];
case 'Top':
return [0, -1];
case 'Bottom':
return [0, 1];
}
}
export function _findControlPoint(point, sourcePoint, targetPoint, _so, _to) {
// 曲率,可配置的
let majorAnchor = 10;
// 偏移,定死的
let minorAnchor = 10;
let result = [];
// 特殊处理完全水平和垂直的情况
if (sourcePoint.pos[0] === targetPoint.pos[0] && _so[1] !== _to[1] && _so[0] === 0 && _to[0] === 0) {
result = [point[0], point[1] + majorAnchor * _so[1]];
return result;
}
if (sourcePoint.pos[1] === targetPoint.pos[1] && _so[0] !== _to[0] && _so[1] === 0 && _to[1] === 0) {
result = [point[0] + majorAnchor * _so[0], point[1]];
return result;
}
// 平常情况
var perpendicular = _so[0] !== _to[0] || _so[1] === _to[1];
if (!perpendicular) {
if (_so[0] === 0) {
result.push(sourcePoint.pos[0] < targetPoint.pos[0] ? point[0] + minorAnchor : point[0] - minorAnchor);
} else {
result.push(point[0] - (majorAnchor * _so[0]));
}
if (_so[1] === 0) {
result.push(sourcePoint.pos[1] < targetPoint.pos[1] ? point[1] + minorAnchor : point[1] - minorAnchor);
} else {
result.push(point[1] - (majorAnchor * _to[1]));
}
} else {
if (_to[0] === 0) {
result.push(targetPoint.pos[0] < sourcePoint.pos[0] ? point[0] + minorAnchor : point[0] - minorAnchor);
} else {
result.push(point[0] + (majorAnchor * _to[0]));
}
if (_to[1] === 0) {
result.push(targetPoint.pos[1] < sourcePoint.pos[1] ? point[1] + minorAnchor : point[1] - minorAnchor);
} else {
result.push(point[1] + (majorAnchor * _so[1]));
}
}
return result;
}
//二阶贝塞尔曲线
export function _findSecondControlPoint(sourcePoint, targetPoint, _so, _to, shapeType) {
//中点
let midX = (sourcePoint.pos[0] + targetPoint.pos[0]) / 2;
let midY = (sourcePoint.pos[1] + targetPoint.pos[1]) / 2;
//四分之一点的位置
let quarterX = midX - ((midX - sourcePoint.pos[0]) / 2);
let quarterY = midY - ((midY - sourcePoint.pos[1]) / 2);
//四分之三位置
let threeQuarterX = midX + ((midX - sourcePoint.pos[0]) / 2);
let threeQuarterY = midY + ((midY - sourcePoint.pos[1]) / 2);
let basicPointX = midX;
let basicPointY = midY;
if (shapeType === "Bezier2-1") {
basicPointX = midX;
basicPointY = midY;
} else if (shapeType === "Bezier2-2") {
basicPointX = quarterX;
basicPointY = quarterY;
} else if (shapeType === "Bezier2-3") {
basicPointX = threeQuarterX;
basicPointY = threeQuarterY;
}
let ctrlPoint;
let offset;
let _width = Math.abs(sourcePoint.pos[0] - sourcePoint.pos[0]);
let _height = Math.abs(sourcePoint.pos[1] - targetPoint.pos[1]);
const dist = Math.sqrt(_width * _width + _height * _height);
//正常情况
let midK = (targetPoint.pos[1] - sourcePoint.pos[1]) / (targetPoint.pos[0] - sourcePoint.pos[0]);
if (midK === 0) {
if (sourcePoint.pos[0] < targetPoint.pos[0] && _so[0] === 1 && _to[0] === -1 ||
sourcePoint.pos[0] > targetPoint.pos[0] && _so[0] === -1 && _to[0] === 1 ||
sourcePoint.pos[1] < targetPoint.pos[1] && _so[1] === 1 && _to[1] === -1 ||
sourcePoint.pos[1] > targetPoint.pos[1] && _so[1] === -1 && _to[1] === 1) {
return ctrlPoint = [midX, midY];
}
}
let k = -1 / midK;
let b = basicPointY - k * basicPointX;
offset = Math.sqrt(3) * dist / 6;
let t;
let _sum0 = _so[0] + _to[0];
let _sum1 = _so[1] + _to[1];
if (targetPoint.pos[0] < sourcePoint.pos[0] && targetPoint.pos[1] < sourcePoint.pos[1] && ((_sum0 === 0 && _sum1 ===
0) || (_sum0 === 1 && _sum1 === -1))) {
t = 1;
} else if (targetPoint.pos[0] > sourcePoint.pos[0] && targetPoint.pos[1] < sourcePoint.pos[1] && (_sum0 === 1 &&
_sum1 === 1)) {
t = 1;
} else if (targetPoint.pos[0] < sourcePoint.pos[0] && targetPoint.pos[1] > sourcePoint.pos[1] && ((_sum0 === 0 &&
_sum1 === 0) || (_sum0 === 1 && _sum1 === 1))) {
t = 1;
} else if (targetPoint.pos[0] > sourcePoint.pos[0] && targetPoint.pos[1] > sourcePoint.pos[1] && (_sum0 === 1 &&
_sum1 === -1)) {
t = 1;
} else {
t = -1;
}
ctrlPoint = [basicPointX + (offset * t), (k * (basicPointX + (offset * t))) + b];
//特殊情况:
let percent = 0.25;
let minorDist = 100;
offset = dist * percent + minorDist;
let so_offsetX = 0;
let so_offsetY = 0;
let to_offsetX = 0;
let to_offsetY = 0;
if (_so[0] === -1 && targetPoint.pos[0] > sourcePoint.pos[0] ||
_so[0] === 1 && targetPoint.pos[0] < sourcePoint.pos[0] ||
_so[1] === 1 && targetPoint.pos[1] < sourcePoint.pos[1] ||
_so[1] === -1 && targetPoint.pos[1] > sourcePoint.pos[1]) {
if (_so[0] !== 0) {
so_offsetX = offset * _so[0];
}
if (_so[1] !== 0) {
so_offsetY = offset * _so[1];
}
return ctrlPoint = [sourcePoint.pos[0] + so_offsetX, sourcePoint.pos[1] + so_offsetY];
}
if (_so[0] === _to[0] && _so[1] === _to[1] ||
_so[0] !== 0 && targetPoint.pos[1] < sourcePoint.pos[1] && _to[1] === -1 ||
_so[0] !== 0 && targetPoint.pos[1] > sourcePoint.pos[1] && _to[1] === 1 ||
_so[1] !== 0 && targetPoint.pos[0] < sourcePoint.pos[0] && _to[0] === -1 ||
_so[1] !== 0 && targetPoint.pos[0] > sourcePoint.pos[0] && _to[0] === 1
) {
if (_to[0] !== 0) {
to_offsetX = offset * _to[0];
} else if (_to[1] !== 0) {
to_offsetY = offset * _to[1];
}
ctrlPoint = [targetPoint.pos[0] + to_offsetX, targetPoint.pos[1] + to_offsetY];
}
return ctrlPoint;
}
export function _findManhattanPoint (points, pos) {
let result = undefined;
let gap = Infinity;
for(let i = 0; i < points.length - 1; i++) {
let _dir = points[i].x === points[i + 1].x ? 'vertical' : 'horizontal';
let _from = points[i];
let _to = points[i + 1];
if (_dir === 'vertical') {
if (gap > Math.abs(pos.x - _from.x)) {
gap = Math.abs(pos.x - _from.x);
result = {
from: _from,
to: _to,
direction: _dir,
index: i
}
}
} else {
if (gap > Math.abs(pos.y - _from.y)) {
gap = Math.abs(pos.y - _from.y);
result = {
from: _from,
to: _to,
direction: _dir,
index: i
}
}
}
}
return result;
}