diff --git a/kcui/README.md b/kcui/README.md new file mode 100644 index 0000000..5c4d29f --- /dev/null +++ b/kcui/README.md @@ -0,0 +1,24 @@ +# testvue + +## Project setup +``` +npm install +``` + +### Compiles and hot-reloads for development +``` +npm run serve +``` + +### Compiles and minifies for production +``` +npm run build +``` + +### Lints and fixes files +``` +npm run lint +``` + +### Customize configuration +See [Configuration Reference](https://cli.vuejs.org/config/). diff --git a/kcui/babel.config.js b/kcui/babel.config.js new file mode 100644 index 0000000..e955840 --- /dev/null +++ b/kcui/babel.config.js @@ -0,0 +1,5 @@ +module.exports = { + presets: [ + '@vue/cli-plugin-babel/preset' + ] +} diff --git a/kcui/jsconfig.json b/kcui/jsconfig.json new file mode 100644 index 0000000..4aafc5f --- /dev/null +++ b/kcui/jsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "esnext", + "baseUrl": "./", + "moduleResolution": "node", + "paths": { + "@/*": [ + "src/*" + ] + }, + "lib": [ + "esnext", + "dom", + "dom.iterable", + "scripthost" + ] + } +} diff --git a/kcui/package.json b/kcui/package.json new file mode 100644 index 0000000..4761c79 --- /dev/null +++ b/kcui/package.json @@ -0,0 +1,47 @@ +{ + "name": "testvue", + "version": "0.1.0", + "private": true, + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build", + "lint": "vue-cli-service lint" + }, + "dependencies": { + "axios": "^1.7.9", + "core-js": "^3.8.3", + "element-ui": "^2.15.14", + "js-cookie": "^3.0.5", + "vue": "^2.6.14", + "vue-router": "^3.5.1" + }, + "devDependencies": { + "@babel/core": "^7.12.16", + "@babel/eslint-parser": "^7.12.16", + "@vue/cli-plugin-babel": "~5.0.0", + "@vue/cli-plugin-eslint": "~5.0.0", + "@vue/cli-service": "~5.0.0", + "eslint": "^7.32.0", + "eslint-plugin-vue": "^8.0.3", + "vue-template-compiler": "^2.6.14" + }, + "eslintConfig": { + "root": true, + "env": { + "node": true + }, + "extends": [ + "plugin:vue/essential", + "eslint:recommended" + ], + "parserOptions": { + "parser": "@babel/eslint-parser" + }, + "rules": {} + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not dead" + ] +} diff --git a/kcui/public/favicon.ico b/kcui/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/kcui/public/favicon.ico differ diff --git a/kcui/public/index.html b/kcui/public/index.html new file mode 100644 index 0000000..3e5a139 --- /dev/null +++ b/kcui/public/index.html @@ -0,0 +1,17 @@ + + + + + + + + <%= htmlWebpackPlugin.options.title %> + + + +
+ + + diff --git a/kcui/src/App.vue b/kcui/src/App.vue new file mode 100644 index 0000000..51165bd --- /dev/null +++ b/kcui/src/App.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/kcui/src/api/login.js b/kcui/src/api/login.js new file mode 100644 index 0000000..02d4156 --- /dev/null +++ b/kcui/src/api/login.js @@ -0,0 +1,34 @@ +import request from "@/utils/request"; + +// 登录方法 +export function userLogin() { + return request({ + url: '/user/doLogin', + method: 'get', + }) +} + + +export function test() { + return request({ + url: '/user/test', + method: 'get', + }) +} + + +//获取图谱数据 +// export function getgraphInfo(data) { +// return request({ +// url: '/user/getDomainGraphTest', +// method: 'post', +// data: data +// }) +// } +export function getgraphInfo(data) { + return request({ + url: '/graph/getDomainGraphTest', + method: 'post', + data: data + }) +} diff --git a/kcui/src/api/upload.js b/kcui/src/api/upload.js new file mode 100644 index 0000000..922d0b6 --- /dev/null +++ b/kcui/src/api/upload.js @@ -0,0 +1,10 @@ +//import axios from '../axios' +import request from '@/utils/request'; + +export const uploadInfo = (params) => { + return request({ + url: '/api/updateload/upload', + method: 'post', + data: params, + }); +}; diff --git a/kcui/src/assets/defaultConfig.js b/kcui/src/assets/defaultConfig.js new file mode 100644 index 0000000..070c553 --- /dev/null +++ b/kcui/src/assets/defaultConfig.js @@ -0,0 +1,30 @@ +export const config = { + background:'40,47,183', + node: { //节点的默认配置 + label: { //标签配置 + show: false, //是否显示 + color: '20,20,20', //字体颜色 + font: 'normal 30px Arial', //字体大小及类型 + textPosition: 'Bottom_Center' //文字位置 Top_Center,Bottom_Center,Middle_Right,Middle_Center + }, + shape: 'circle', + showLabel:false, + }, + link: { //连线的默认配置 + label: { //连线标签 + show: true, //是否显示 + color: '255,255,255', //字体颜色 + font: 'normal 13px Arial', //字体大小及类型 + //background:'220,220,220' //标签背景色 + }, + lineType: 'direct', //连线类型,direct,curver,vlink,hlink,vbezier,hbezier,bezier + colorType: 'defined', //连线颜色类型 source:继承source颜色,target:继承target颜色 both:用双边颜色,defined:自定义 + alpha: 1, // 连线透明度 + showArrow: true, //显示连线箭头 + }, + lineColor:'202,202,202', + layerName:'frDirect', + + wheelZoom: 0.8, //开启鼠标滚轮缩放 + highLightNeiber: false //相邻节点高亮开关 +} diff --git a/kcui/src/assets/js/graphvis.layout.min.js b/kcui/src/assets/js/graphvis.layout.min.js new file mode 100644 index 0000000..a4e7714 --- /dev/null +++ b/kcui/src/assets/js/graphvis.layout.min.js @@ -0,0 +1,4639 @@ +/* eslint-disable */ +(function () { + var ARFLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.neighborAttraction = 3.0; + this.attraction = 0.03; + this.forceScale = 0.0; + this.deltaT = 5.0; + this.forceCutoff = 10.0; + this.neighbers = {}; + this.inited = false; + }; + ARFLayout.prototype.getConfig = function () { + return [ + { label: "邻点引力", neighborAttraction: 8.0 }, + { label: "引力", attraction: 0.05 }, + { label: "力缩放系数", forceScale: 8.0 }, + ]; + }; + ARFLayout.prototype.resetConfig = function (layoutConfig) { + var _self = this; + if (layoutConfig) { + this.neighborAttraction = + Number(layoutConfig["neighborAttraction"]) || 8.0; + this.attraction = Number(layoutConfig["attraction"]) || 0.05; + this.forceScale = Number(layoutConfig["forceScale"]) || 8.0; + this.neighbers = {}; + _self.nodes.forEach(function (n) { + n.degree = (n.inLinks || []).length + (n.outLinks || []).length; + }); + } + this.inited = true; + }; + ARFLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + ARFLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + ARFLayout.prototype.initAlgo = function () { + var _self = this; + _self.neighborAttraction = 8.0; + _self.attraction = 0.12; + _self.forceScale = 5.0; + _self.deltaT = 5.0; + _self.forceCutoff = 10.0; + _self.nodes.forEach(function (n) { + var inLinks = n.inLinks || []; + var outLinks = n.outLinks || []; + n.degree = inLinks.length + outLinks.length; + }); + this.inited = true; + }; + ARFLayout.prototype.goAlgo = function () { + var _self = this; + var minX = Infinity, + minY = Infinity; + _self.nodes.forEach(function (node) { + var f = _self.getForceforNode(node); + var degree = node.degree; + var deltaIndividual = + degree <= 1 ? _self.deltaT : _self.deltaT / Math.pow(degree, 0.4); + f = { x: f.x * deltaIndividual, y: f.y * deltaIndividual }; + node.x += f.x; + node.y += f.y; + minX = Math.min(minX, node.x); + minY = Math.min(minY, node.y); + }); + _self.nodes.forEach(function (node) { + node.x += 100 - minX; + node.y += 100 - minY; + }); + }; + ARFLayout.prototype.getForceforNode = function (node) { + var _self = this; + var numNodes = _self.nodes.length; + var mDot = { x: 0, y: 0 }; + if (node.x == 0 && node.y == 0) { + return mDot; + } + _self.nodes.forEach(function (n) { + if (node.id != n.id && (n.x != 0 || n.y != 0)) { + var tempX = n.x - node.x; + var tempY = n.y - node.y; + if (tempX == 0 && tempY == 0) { + tempX = 50; + tempY = 50; + } + var multiplier = 1.0; + if (_self.isAdjacent(node, n)) { + multiplier = _self.neighborAttraction; + } + multiplier = multiplier * (_self.attraction / Math.sqrt(numNodes)); + mDot = { + x: mDot.x + tempX * multiplier, + y: mDot.y + tempY * multiplier, + }; + multiplier = 1.0 / Math.sqrt(tempX * tempX + tempY * tempY); + mDot = { + x: mDot.x - tempX * multiplier * _self.forceScale, + y: mDot.y - tempY * multiplier * _self.forceScale, + }; + } + }); + var distance = _self.distance(0.0, 0.0, mDot.x, mDot.y); + if (distance > _self.forceCutoff) { + var mult = _self.forceCutoff / distance; + mDot = { x: mDot.x * mult, y: mDot.y * mult }; + } + return mDot; + }; + ARFLayout.prototype.getDegree = function (node) { + return (node.inLinks || []).length + (node.outLinks || []).length; + }; + ARFLayout.prototype.isAdjacent = function (node, otherNode) { + var neighbers = []; + (node.inLinks || []).forEach(function (l) { + neighbers.push(l.source); + }); + (node.outLinks || []).forEach(function (l) { + neighbers.push(l.target); + }); + var flag = false; + neighbers.forEach(function (n) { + if (n.id == otherNode.id) { + flag = true; + } + }); + }; + ARFLayout.prototype.distance = function (px, py, x, y) { + px -= x; + py -= y; + return Math.sqrt(px * px + py * py); + }; + var CircleLayout = function (nodes, links) { + this.nodes = nodes; + this.diameter = 500; + this.boolfixeddiameter = false; + this.boolTransition = true; + this.cumethod = "auto"; + this.scale = 1.2; + this.TWO_PI = Math.PI * 2; + this.center = [0, 0]; + this.intSteps = 50; + this.inited = false; + }; + CircleLayout.prototype.getConfig = function () { + return [ + { + label: "直径计算", + cumethod: [ + { label: "自动", value: "auto" }, + { label: "指定", value: "metal" }, + ], + }, + { label: "直径大小", diameter: 500 }, + { label: "中心位置", center: [0, 0] }, + ]; + }; + CircleLayout.prototype.resetConfig = function (layoutConfig) { + this.diameter = Number(layoutConfig["diameter"]) || 500; + this.cumethod = layoutConfig["cumethod"] || "auto"; + this.center = layoutConfig["center"] || [0, 0]; + if (this.cumethod == "auto") { + this.boolfixeddiameter = false; + } else { + this.boolfixeddiameter = true; + } + this.initAlgo(); + }; + CircleLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + CircleLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + CircleLayout.prototype.initAlgo = function () { + var _self = this; + var nodes = _self.nodes; + var nodeCount = nodes.length; + var nodeCoords = []; + var tempcirc = 0.0; + var temdiameter = 0.0; + var index = 0; + var noderadius = 0.0; + var theta = _self.TWO_PI / nodeCount; + var lasttheta = 0.0; + nodes = nodes.sort(function (n1, n2) { + var x = (n1.inLinks || []).length + (n1.outLinks || []).length; + var y = (n2.inLinks || []).length + (n2.outLinks || []).length; + if (x > y) { + return -1; + } else if (x < y) { + return 1; + } else { + return 0; + } + }); + if (!_self.boolfixeddiameter) { + for (var i = 0; i < nodeCount; i++) { + var n = nodes[i]; + tempcirc += n.scaleX * n.radius * 2 * 1.2; + } + tempcirc *= _self.scale; + temdiameter = tempcirc / Math.PI; + theta = _self.TWO_PI / tempcirc; + } else { + temdiameter = _self.diameter; + } + var radius = temdiameter / 2; + for (var i = 0; i < nodeCount; i++) { + var n = nodes[i]; + if (!_self.boolfixeddiameter) { + noderadius = n.scaleX * n.radius * 2; + var noderadian = theta * noderadius * _self.scale; + nodeCoords = _self.cartCoors(radius, 1, lasttheta + noderadian); + lasttheta += noderadius * 1.2 * theta * _self.scale; + } else { + nodeCoords = _self.cartCoors(radius, index, theta); + } + var posData = _self.newLayoutData(); + posData.finishx = nodeCoords[0]; + posData.finishy = nodeCoords[1]; + posData.xdistance = (1.0 / _self.intSteps) * (nodeCoords[0] - n.x); + posData.ydistance = (1.0 / _self.intSteps) * (nodeCoords[1] - n.y); + n.layoutData = posData; + index++; + } + this.inited = true; + }; + CircleLayout.prototype.cartCoors = function (radius, whichInt, theta) { + var coOrds = []; + coOrds[0] = this.center[0] + radius * Math.cos(theta * whichInt); + coOrds[1] = this.center[1] + radius * Math.sin(theta * whichInt); + return coOrds; + }; + CircleLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var DualCircleLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.highdegreeoutside = false; + this.secondarynodecount = 15; + this.boolNoOverlap = true; + this.boolTransition = true; + this.TWO_PI = Math.PI * 2; + this.intSteps = 50; + this.inited = false; + }; + DualCircleLayout.prototype.getConfig = function () { + return [ + { + label: "分布位置", + position: [ + { label: "环内部", value: "inside" }, + { label: "环外部", value: "outside" }, + ], + }, + { label: "核心数", secondarynodecount: 15 }, + ]; + }; + DualCircleLayout.prototype.resetConfig = function (layoutConfig) { + this.secondarynodecount = Number(layoutConfig["secondarynodecount"]) || 15; + this.position = layoutConfig["position"] || "inside"; + if (this.position == "inside") { + this.highdegreeoutside = false; + } else { + this.highdegreeoutside = true; + } + this.initAlgo(); + }; + DualCircleLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + DualCircleLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + DualCircleLayout.prototype.initAlgo = function () { + var _self = this; + var nodes = _self.nodes; + var nodeCounts = nodes.length; + var nodeCoords = []; + var tmpsecondarycirc = 0, + tmpprimarycirc = 0; + var lasttheta = 0, + secondary_theta = 0, + correct_theta = 0; + var primary_scale = 1, + secondry_scale = 1; + if (_self.secondarynodecount > nodeCounts) { + _self.secondarynodecount = 1; + } + nodes = nodes.sort(function (n1, n2) { + var x = (n1.inLinks || []).length + (n1.outLinks || []).length; + var y = (n2.inLinks || []).length + (n2.outLinks || []).length; + if (x > y) { + return -1; + } else if (x < y) { + return 1; + } else { + return 0; + } + }); + for (var i = 0; i < nodeCounts; i++) { + var n = nodes[i]; + var noderadius = n.scaleX * n.radius; + if (i < _self.secondarynodecount) { + tmpsecondarycirc += noderadius * 2.0; + } else { + tmpprimarycirc += noderadius * 2.0; + } + } + var circum_ratio = tmpprimarycirc / tmpsecondarycirc; + if (circum_ratio < 2) { + primary_scale = 2 / circum_ratio; + tmpprimarycirc = 2 * tmpsecondarycirc; + } + if (_self.highdegreeoutside) { + secondry_scale = (2 * tmpprimarycirc) / tmpsecondarycirc; + tmpsecondarycirc = tmpprimarycirc * 2; + } else { + secondry_scale = tmpprimarycirc / (2 * tmpsecondarycirc); + tmpsecondarycirc = tmpprimarycirc / 2; + } + tmpprimarycirc *= 1.2; + primary_theta = _self.TWO_PI / tmpprimarycirc; + var primaryradius = tmpprimarycirc / Math.PI / 2; + tmpsecondarycirc *= 1.2; + secondary_theta = _self.TWO_PI / tmpsecondarycirc; + var secondaryradius = tmpsecondarycirc / Math.PI / 2; + for (var i = 0; i < nodeCounts; i++) { + var n = nodes[i]; + var noderadius = n.scaleX * n.radius; + if (i < _self.secondarynodecount) { + if (secondry_scale > 2) { + noderadius = + tmpsecondarycirc / + (2 * _self.secondarynodecount * secondry_scale * 1.2); + } + var noderadian = secondary_theta * noderadius * 1.2 * secondry_scale; + if (i == 0) { + correct_theta = noderadian; + } + nodeCoords = _self.cartCoors( + secondaryradius, + 1, + lasttheta + noderadian - correct_theta, + ); + lasttheta += noderadius * 2 * secondary_theta * 1.2 * secondry_scale; + } else { + var noderadian = primary_theta * noderadius * 1.2 * primary_scale; + if (i == _self.secondarynodecount) { + lasttheta = 0; + correct_theta = noderadian; + } + nodeCoords = _self.cartCoors( + primaryradius, + 1, + lasttheta + noderadian - correct_theta, + ); + lasttheta += noderadius * 2 * primary_theta * 1.2 * primary_scale; + } + var posData = _self.newLayoutData(); + posData.finishx = nodeCoords[0]; + posData.finishy = nodeCoords[1]; + posData.xdistance = (1.0 / _self.intSteps) * (nodeCoords[0] - n.x); + posData.ydistance = (1.0 / _self.intSteps) * (nodeCoords[1] - n.y); + n.layoutData = posData; + } + this.inited = true; + }; + DualCircleLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + DualCircleLayout.prototype.cartCoors = function (radius, whichInt, theta) { + var coOrds = []; + coOrds[0] = radius * Math.cos(theta * whichInt + Math.PI / 2); + coOrds[1] = radius * Math.sin(theta * whichInt + Math.PI / 2); + return coOrds; + }; + var LayerLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.outCircleNodes = 11; + this.layerDistance = 30; + this.boolTransition = true; + this.intSteps = 50; + this.inited = false; + }; + LayerLayout.prototype.getConfig = function () { + return [ + { label: "外层点数", outCircleNodes: 11 }, + { label: "层间距", layerDistance: 30 }, + ]; + }; + LayerLayout.prototype.resetConfig = function (layoutConfig) { + this.outCircleNodes = Number(layoutConfig["outCircleNodes"]) || 11; + this.layerDistance = Number(layoutConfig["layerDistance"]) || 30; + this.initAlgo(); + }; + LayerLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + LayerLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + LayerLayout.prototype.initAlgo = function () { + var _self = this; + var nodes = _self.nodes; + var nodeCount = nodes.length; + var innerCircleRaduis = 0, + nextLayerRoundLong = 0, + currentRoundLong = 0; + var maxTheta = 0, + theta = 0; + nodes = nodes.sort(function (n1, n2) { + var x = (n1.inLinks || []).length + (n1.outLinks || []).length; + var y = (n2.inLinks || []).length + (n2.outLinks || []).length; + if (x < y) { + return -1; + } else if (x > y) { + return 1; + } else { + return 0; + } + }); + if (_self.outCircleNodes > nodeCount) { + _self.outCircleNodes = 0; + } + for (var i = 0; i < nodeCount; i++) { + var node = nodes[i]; + currentRoundLong += node.radius * node.scaleX; + if (currentRoundLong > nextLayerRoundLong) { + nextLayerNodeRaduis = node.radius * node.scaleX; + nextCircleRaduis = + innerCircleRaduis + _self.layerDistance + nextLayerNodeRaduis; + nextLayerRoundLong = 2 * Math.PI * nextCircleRaduis; + innerCircleRaduis = + innerCircleRaduis + _self.layerDistance + node.radius * node.scaleX; + theta = 1.0 / nextCircleRaduis; + maxTheta = 0; + currentRoundLong = node.radius * node.scaleX; + } + var thisAngle = 0; + if (i < nodeCount - _self.outCircleNodes) { + thisAngle = theta * node.radius * node.scaleX; + } else { + nextCircleRaduis = + innerCircleRaduis + _self.layerDistance + nextLayerNodeRaduis; + thisAngle = (2 * Math.PI) / _self.outCircleNodes; + } + maxTheta += thisAngle; + var posData = _self.newLayoutData(); + posData.finishx = nextCircleRaduis * 2.4 * Math.cos(maxTheta + Math.PI); + posData.finishy = nextCircleRaduis * 2.4 * Math.sin(maxTheta + Math.PI); + posData.xdistance = (1.0 / _self.intSteps) * (posData.finishx - node.x); + posData.ydistance = (1.0 / _self.intSteps) * (posData.finishy - node.y); + node.layoutData = posData; + } + this.inited = true; + }; + LayerLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var FRlayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.AREA_MULTIPLICATOR = 10000; + this.area = 800; + this.gravity = 1.2; + this.SPEED_DIVISOR = 800.0; + this.speed = 10; + this.inited = false; + }; + FRlayout.prototype.newLayoutData = function () { + var layoutData = { + dx: 0.0, + dy: 0.0, + old_dx: 0.0, + old_dy: 0.0, + freeze: 0.0, + }; + return layoutData; + }; + FRlayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + FRlayout.prototype.getConfig = function () { + return [ + { label: "区域大小", area: 500 }, + { label: "重力", gravity: 2.0 }, + ]; + }; + FRlayout.prototype.resetConfig = function (layoutConfig) { + var _self = this; + if (layoutConfig) { + this.area = Number(layoutConfig["area"]) || 500; + this.gravity = Number(layoutConfig["gravity"]) || 1.5; + this.nodes.forEach(function (n) { + n.layoutData = _self.newLayoutData(); + }); + } + this.inited = true; + }; + FRlayout.prototype.initAlgo = function () { + var _self = this; + _self.area = _self.nodes.length / 2; + _self.nodes.forEach(function (n) { + n.layoutData = _self.newLayoutData(); + }); + this.inited = true; + }; + FRlayout.prototype.goAlgo = function () { + var _self = this; + var nodes = _self.nodes; + var nodeCount = nodes.length; + var maxDisplace = Math.sqrt(_self.AREA_MULTIPLICATOR * _self.area) / 10.0; + var k = Math.sqrt( + (_self.AREA_MULTIPLICATOR * _self.area) / (1.0 + nodeCount), + ); + nodes.forEach(function (N1, i) { + N1.layoutData.dx = 0; + N1.layoutData.dy = 0; + nodes.forEach(function (N2, j) { + if (i != j) { + var xDist = N1.x - N2.x; + var yDist = N1.y - N2.y; + var dist = Math.sqrt(xDist * xDist + yDist * yDist); + if (dist > 0) { + var repulsiveF = (k * k) / dist; + var layoutData = N1.layoutData; + layoutData.dx += (xDist / dist) * repulsiveF; + layoutData.dy += (yDist / dist) * repulsiveF; + } + } + }); + }); + var links = _self.links; + links.forEach(function (E) { + var Nf = E.source; + var Nt = E.target; + var xDist = Nf.x - Nt.x; + var yDist = Nf.y - Nt.y; + var dist = Math.sqrt(xDist * xDist + yDist * yDist); + var attractiveF = (dist * dist) / k; + if (dist > 0) { + var sourceLayoutData = Nf.layoutData; + var targetLayoutData = Nt.layoutData; + sourceLayoutData.dx -= (xDist / dist) * attractiveF; + sourceLayoutData.dy -= (yDist / dist) * attractiveF; + targetLayoutData.dx += (xDist / dist) * attractiveF; + targetLayoutData.dy += (yDist / dist) * attractiveF; + } + }); + nodes.forEach(function (n) { + var layoutData = n.layoutData; + var d = Math.sqrt(n.x * n.x + n.y * n.y); + var gf = 0.01 * k * _self.gravity * d; + layoutData.dx -= (gf * n.x) / d; + layoutData.dy -= (gf * n.y) / d; + layoutData.dx *= _self.speed / _self.SPEED_DIVISOR; + layoutData.dy *= _self.speed / _self.SPEED_DIVISOR; + var dist = Math.sqrt( + layoutData.dx * layoutData.dx + layoutData.dy * layoutData.dy, + ); + if (dist > 0) { + var limitedDist = Math.min( + maxDisplace * (_self.speed / _self.SPEED_DIVISOR), + dist, + ); + n.x += (layoutData.dx / dist) * limitedDist; + n.y += (layoutData.dy / dist) * limitedDist; + } + }); + }; + var FruchtermanReingoldLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.config = { + autoArea: true, + area: 1000, + gravity: 3, + speed: 0.08, + iterations: 1000, + }; + this.maxDisplace = 10; + this.k = 120.0; + this.currentIter = 0; + this.inited = false; + }; + FruchtermanReingoldLayout.prototype.getConfig = function () { + var self = this; + return [ + { label: "重力", gravity: 0.5 }, + { label: "边长度", k: self.k }, + { label: "收敛速度", speed: 0.5 }, + ]; + }; + FruchtermanReingoldLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.nodes.forEach(function (node) { + node.fr_x = node.x; + node.fr_y = node.y; + node.fr = { dx: 0, dy: 0 }; + }); + this.config.gravity = Number(layoutConfig["gravity"]) || 0.5; + this.config.speed = Number(layoutConfig["speed"]) || 0.08; + this.k = Number(layoutConfig["k"]) || 120; + this.currentIter = 0; + } + this.inited = true; + }; + FruchtermanReingoldLayout.prototype.initAlgo = function () { + var self = this; + var nodesCount = this.nodes.length; + self.nodes.forEach(function (node) { + node.fr_x = node.x; + node.fr_y = node.y; + node.fr = { dx: 0, dy: 0 }; + }); + self.config.area = self.config.autoArea + ? nodesCount * nodesCount + : self.config.area; + self.maxDisplace = Math.sqrt(self.config.area) / 8; + self.k = Math.sqrt(self.config.area / (1 + nodesCount)); + if (self.maxDisplace < 150) { + self.maxDisplace = 150; + } + if (self.k < 40) { + self.k = 40; + } + self.currentIter = 0; + this.inited = true; + }; + FruchtermanReingoldLayout.prototype.runLayout = function () { + if (this.currentIter > this.config.iterations) { + return; + } + if (this.inited) { + this.goAlgo(); + this.currentIter++; + } + }; + FruchtermanReingoldLayout.prototype.goAlgo = function () { + var self = this; + var nodes = self.nodes; + var links = self.links; + var nodesCount = self.nodes.length; + for (var i = 0; i < nodesCount; i++) { + var n = nodes[i]; + for (var j = 0; j < nodesCount; j++) { + var n2 = nodes[j]; + if (n.id != n2.id) { + var xDist = n.fr_x - n2.fr_x; + var yDist = n.fr_y - n2.fr_y; + var dist = Math.sqrt(xDist * xDist + yDist * yDist) + 0.01; + if (dist > 0) { + var repulsiveF = (self.k * self.k) / dist; + n.fr.dx += (xDist / dist) * repulsiveF; + n.fr.dy += (yDist / dist) * repulsiveF; + } + } + } + } + var edgesCount = links.length; + for (i = 0; i < edgesCount; i++) { + var link = links[i]; + var nSource = link.source; + var nTarget = link.target; + var xDist = nSource.fr_x - nTarget.fr_x; + var yDist = nSource.fr_y - nTarget.fr_y; + var dist = Math.sqrt(xDist * xDist + yDist * yDist) + 0.01; + var attractiveF = (dist * dist) / self.k; + if (dist > 0) { + nSource.fr.dx -= (xDist / dist) * attractiveF; + nSource.fr.dy -= (yDist / dist) * attractiveF; + nTarget.fr.dx += (xDist / dist) * attractiveF; + nTarget.fr.dy += (yDist / dist) * attractiveF; + } + } + for (var i = 0; i < nodesCount; i++) { + var n = nodes[i]; + var d = Math.sqrt(n.fr_x * n.fr_x + n.fr_y * n.fr_y); + var gf = 0.01 * self.k * self.config.gravity * d; + n.fr.dx -= (gf * n.fr_x) / d; + n.fr.dy -= (gf * n.fr_y) / d; + n.fr.dx *= self.config.speed; + n.fr.dy *= self.config.speed; + if (!n.fixed) { + var xDist = n.fr.dx; + var yDist = n.fr.dy; + dist = Math.sqrt(xDist * xDist + yDist * yDist); + if (dist > 0) { + var limitedDist = Math.min( + self.maxDisplace * self.config.speed, + dist, + ); + n.fr_x += (xDist / dist) * limitedDist; + n.fr_y += (yDist / dist) * limitedDist; + } + } + } + for (var i = 0; i < nodesCount; i++) { + nodes[i].x = nodes[i].fr_x; + nodes[i].y = nodes[i].fr_y; + } + }; + var GirdLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.xOrigin = 1; + this.yOrigin = 1; + this.horizontalScale = 60; + this.verticalScale = 60; + this.horizontal = false; + this.center = [100, 100]; + this.boolTransition = true; + this.intSteps = 50; + this.inited = false; + }; + GirdLayout.prototype.getConfig = function () { + return [ + { label: "水平间距", horizontalScale: 100 }, + { label: "垂直间距", verticalScale: 100 }, + ]; + }; + GirdLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.horizontalScale = Number(layoutConfig["horizontalScale"]) || 100; + this.verticalScale = Number(layoutConfig["verticalScale"]) || 100; + this.center = layoutConfig["center"] || [100, 100]; + this.initAlgo(); + } + }; + GirdLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + GirdLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + GirdLayout.prototype.initAlgo = function () { + var _self = this; + var nodes = _self.nodes; + var nodeCount = nodes.length; + var xGridScales = Math.round(Math.sqrt(nodeCount)) + 1; + var yGridScales = Math.round(Math.sqrt(nodeCount)) + 1; + this.inited = true; + nodes = nodes.sort(function (n1, n2) { + var x = (n1.inLinks || []).length + (n1.outLinks || []).length; + var y = (n2.inLinks || []).length + (n2.outLinks || []).length; + if (x > y) { + return -1; + } else if (x < y) { + return 1; + } else { + return 0; + } + }); + var k = 0; + for (var i = 0; i < xGridScales; i++) { + for (var j = 0; j < yGridScales; j++) { + if (k >= nodeCount) { + continue; + } + var tempX, tempY; + if (_self.horizontal) { + tempX = _self.xGridToScreen(i, j); + tempY = _self.yGridToScreen(i, j); + } else { + tempX = _self.yGridToScreen(i, j); + tempY = -_self.xGridToScreen(i, j); + } + tempX += _self.center[0] - (xGridScales / 2) * _self.horizontalScale; + tempY += + _self.center[1] + + (yGridScales / 2) * _self.verticalScale - + _self.verticalScale; + var node = nodes[k++]; + var posData = _self.newLayoutData(); + posData.finishx = tempX; + posData.finishy = tempY; + posData.xdistance = (1.0 / _self.intSteps) * (tempX - node.x); + posData.ydistance = (1.0 / _self.intSteps) * (tempY - node.y); + node.layoutData = posData; + } + } + }; + GirdLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + GirdLayout.prototype.xGridToScreen = function (xg, yg) { + return this.xOrigin + xg * this.horizontalScale; + }; + GirdLayout.prototype.yGridToScreen = function (xg, yg) { + return this.yOrigin + yg * this.verticalScale; + }; + var KKLayout = function (_nodes, _links) { + this.nodes = _nodes; + this.links = _links; + this.nodeIds = []; + this.VECTOR_D1 = []; + this.VECTOR_D2 = []; + this.lij = []; + this.kij = []; + this.tempNodes = []; + this.realSize = 3000.0; + this.tempSize = 5.0; + this.inited = false; + }; + KKLayout.prototype.getConfig = function () { + var canvasWidth = this.setCanvasSize(this.nodes.length); + return [{ label: "区域大小", realSize: canvasWidth }]; + }; + KKLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.realSize = Number(layoutConfig["realSize"]) || 1000; + this.initAlgo(); + } + }; + KKLayout.prototype.runLayout = function () { + var i = 0; + while (i++ < 100 && this.inited) { + this.goAlgo(); + } + }; + KKLayout.prototype.initAlgo = function () { + var _self = this; + var nodes = _self.nodes; + var nodeCount = nodes.length; + this.inited = true; + var L0 = _self.tempSize; + _self.nodeIds = []; + _self.tempNodes = []; + _self.VECTOR_D1 = []; + _self.VECTOR_D2 = []; + _self.lij = []; + _self.kij = []; + nodes.forEach(function (node) { + _self.nodeIds.push(node.id); + _self.tempNodes.push({ + id: node.id, + x: node.x / (_self.realSize / _self.tempSize), + y: node.y / (_self.realSize / _self.tempSize), + }); + }); + var lij = [nodeCount]; + var kij = [nodeCount]; + var dij = _self.shortPath(nodeCount); + var max_dij = _self.getMaxDij(nodeCount, dij); + _self.getKijLij(L0, max_dij, dij, kij, lij); + var _VECTOR_D1 = [nodeCount]; + var _VECTOR_D2 = [nodeCount]; + _self.tempNodes.forEach(function (nodeM, i) { + var myD1 = 0.0, + myD2 = 0.0; + _self.tempNodes.forEach(function (nodeN, j) { + if (i != j) { + var dx = nodeM.x - nodeN.x; + var dy = nodeM.y - nodeN.y; + var mi_dist = Math.sqrt(dx * dx + dy * dy); + myD1 += kij[i][j] * (dx - (lij[i][j] * dx) / mi_dist); + myD2 += kij[i][j] * (dy - (lij[i][j] * dy) / mi_dist); + } + }); + _VECTOR_D1[i] = myD1; + _VECTOR_D2[i] = myD2; + }); + _self.VECTOR_D1 = _VECTOR_D1; + _self.VECTOR_D2 = _VECTOR_D2; + _self.lij = lij; + _self.kij = kij; + }; + KKLayout.prototype.goAlgo = function () { + var _self = this; + var nodeCount = _self.tempNodes.length; + var epsilon = 0.00000000001; + var myD1 = 0.0, + myD2 = 0.0; + var A = 0.0, + B = 0.0, + C = 0.0; + var delta_x, delta_y; + var old_x, old_y, new_x, new_y; + var m = 0; + var max_delta = -1; + for (var i = 0; i < nodeCount; i++) { + var delta = + _self.VECTOR_D1[i] * _self.VECTOR_D1[i] + + _self.VECTOR_D2[i] * _self.VECTOR_D2[i]; + if (delta > max_delta) { + m = i; + max_delta = delta; + } + } + if (max_delta < epsilon) { + return; + } + var nodeM = _self.tempNodes[m]; + old_x = nodeM.x; + old_y = nodeM.y; + for (var i = 0; i < nodeCount; i++) { + if (i == m) { + continue; + } + var nodeI = _self.tempNodes[i]; + var dx = old_x - nodeI.x; + var dy = old_y - nodeI.y; + var dist = Math.sqrt(dx * dx + dy * dy); + var den = dist * (dx * dx + dy * dy); + A += _self.kij[m][i] * (1.0 - (_self.lij[m][i] * dy * dy) / den); + B += _self.kij[m][i] * ((_self.lij[m][i] * dx * dy) / den); + C += _self.kij[m][i] * (1.0 - (_self.lij[m][i] * dx * dx) / den); + } + myD1 = _self.VECTOR_D1[m]; + myD2 = _self.VECTOR_D2[m]; + delta_y = (B * myD1 - myD2 * A) / (C * A - B * B); + delta_x = -(myD1 + B * delta_y) / A; + new_x = old_x + delta_x; + new_y = old_y + delta_y; + _self.VECTOR_D1[m] = _self.VECTOR_D2[m] = 0.0; + for (var i = 0; i < nodeCount; i++) { + if (i == m) { + continue; + } + var nodeI = _self.tempNodes[i]; + var old_dx = old_x - nodeI.x; + var old_dy = old_y - nodeI.y; + var old_mi_dist = Math.sqrt(old_dx * old_dx + old_dy * old_dy); + var new_dx = new_x - nodeI.x; + var new_dy = new_y - nodeI.y; + var new_mi_dist = Math.sqrt(new_dx * new_dx + new_dy * new_dy); + _self.VECTOR_D1[i] -= + _self.kij[m][i] * (-old_dx + (_self.lij[m][i] * old_dx) / old_mi_dist); + _self.VECTOR_D2[i] -= + _self.kij[m][i] * (-old_dy + (_self.lij[m][i] * old_dy) / old_mi_dist); + _self.VECTOR_D1[i] += + _self.kij[m][i] * (-new_dx + (_self.lij[m][i] * new_dx) / new_mi_dist); + _self.VECTOR_D2[i] += + _self.kij[m][i] * (-new_dy + (_self.lij[m][i] * new_dy) / new_mi_dist); + _self.VECTOR_D1[m] += + _self.kij[m][i] * (new_dx - (_self.lij[m][i] * new_dx) / new_mi_dist); + _self.VECTOR_D2[m] += + _self.kij[m][i] * (new_dy - (_self.lij[m][i] * new_dy) / new_mi_dist); + } + nodeM.x = new_x; + nodeM.y = new_y; + var index = _self.nodeIds.indexOf(nodeM.id); + var node = _self.nodes[index]; + node.x = new_x * (_self.realSize / _self.tempSize); + node.y = new_y * (_self.realSize / _self.tempSize); + }; + KKLayout.prototype.getMaxDij = function (nodeCount, dij) { + var max_dij = 0; + for (var i = 0; i < nodeCount; i++) { + for (var j = i + 1; j < nodeCount; j++) { + if (dij[i][j] == Infinity) { + continue; + } + if (dij[i][j] > max_dij) { + max_dij = dij[i][j]; + } + } + } + for (var i = 0; i < nodeCount; i++) { + for (var j = 0; j < nodeCount; j++) { + if (dij[i][j] == Infinity) { + dij[i][j] = max_dij; + } + } + } + return max_dij; + }; + KKLayout.prototype.getKijLij = function (L0, max_dij, dij, kij, lij) { + var L = L0 / max_dij; + var nodeCount = this.tempNodes.length; + for (var i = 0; i < nodeCount; i++) { + kij[i] = [nodeCount]; + lij[i] = [nodeCount]; + for (var j = 0; j < nodeCount; j++) { + var tmp = dij[i][j] * dij[i][j]; + if (i == j) { + continue; + } + kij[i][j] = (Math.pow(nodeCount, 2) * 1.0) / tmp; + lij[i][j] = L * dij[i][j]; + } + } + }; + KKLayout.prototype.shortPath = function (nodeCount) { + var _self = this; + var dij = [nodeCount]; + for (var i = 0; i < nodeCount; i++) { + dij[i] = [nodeCount]; + for (var j = 0; j < nodeCount; j++) { + if (i == j) { + dij[i][j] = 0; + continue; + } + dij[i][j] = Infinity; + } + } + _self.links.forEach(function (link) { + var i = _self.nodeIds.indexOf(link.source.id); + var j = _self.nodeIds.indexOf(link.target.id); + dij[i][j] = 1; + dij[j][i] = 1; + }); + for (var k = 0; k < nodeCount; k++) { + for (var i = 0; i < nodeCount; i++) { + for (var j = i + 1; j < nodeCount; j++) { + var temp = dij[i][k] + dij[k][j]; + if (temp < dij[i][j]) { + dij[i][j] = temp; + dij[j][i] = temp; + } + } + } + } + return dij; + }; + KKLayout.prototype.setCanvasSize = function (nodeCount) { + var maxWidth = 8000; + var minWidth = 1500; + var widthRange = maxWidth - minWidth; + var shiftLog = 5; + var maxLog = Math.log(800 + shiftLog); + var minLog = Math.log(shiftLog); + var logRange = maxLog - minLog; + var canvasWidth = Math.round( + ((Math.log(Math.min(nodeCount, 8000) / 10 + shiftLog) - minLog) * + widthRange) / + logRange + + minWidth, + ); + return canvasWidth; + }; + var LayeredLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.layerDistance = 80; + this.ajustSize = true; + this.boolTransition = true; + this.intSteps = 50; + this.inited = false; + }; + LayeredLayout.prototype.getConfig = function () { + return [{ label: "层间距", layerDistance: 80 }]; + }; + LayeredLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.layerDistance = Number(layoutConfig["layerDistance"]) || 100; + this.initAlgo(); + } + }; + LayeredLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + LayeredLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + LayeredLayout.prototype.initAlgo = function () { + var _self = this; + var nodes = _self.nodes; + var nodeCount = nodes.length; + var innerCircleRaduis = 0, + nextLayerRoundLong = 0, + currentRoundLong = 0; + var maxTheta = 0, + theta = 0; + this.inited = true; + nodes = nodes.sort(function (n1, n2) { + var x = (n1.inLinks || []).length + (n1.outLinks || []).length; + var y = (n2.inLinks || []).length + (n2.outLinks || []).length; + if (x < y) { + return -1; + } else if (x > y) { + return 1; + } else { + return 0; + } + }); + var startValue = _self.getValue(nodes[0]); + var startX = nodes[0].x; + var startY = nodes[0].y; + var currentValue = startValue; + var isFirstlayer = true; + var shiftFirstlayer = 0; + var currentOrbit = []; + nodes.forEach(function (n) { + if (_self.getValue(n) != currentValue) { + if (isFirstlayer && currentOrbit.length > 1) { + shiftFirstlayer = 1; + } + isFirstlayer = false; + _self.renderOrbit( + currentOrbit, + startX, + startY, + shiftFirstlayer + (currentValue - startValue), + ); + currentOrbit = []; + currentValue = _self.getValue(n); + } + currentOrbit.push(n); + }); + if (currentOrbit.length > 0) { + _self.renderOrbit( + currentOrbit, + startX, + startY, + shiftFirstlayer + (currentValue - startValue), + ); + } + }; + LayeredLayout.prototype.getValue = function (node) { + return (node.inLinks || []).length + (node.outLinks || []).length; + }; + LayeredLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + LayeredLayout.prototype.renderOrbit = function ( + currentOrbit, + startX, + startY, + radius, + ) { + var _self = this; + if (_self.ajustSize) { + let length = 0; + currentOrbit.forEach(function (n) { + length += n.radius; + }); + var currentAngle = 0; + var shift = 360 / length; + currentOrbit.forEach(function (o, i) { + currentAngle += (shift * o.radius) / 2; + var noise = 0; + if (i % 3 == 1) { + noise = o.radius * -1; + } + if (i % 3 == 2) { + noise = o.radius; + } + var x = + startX + + (_self.layerDistance * radius + noise) * + Math.cos(currentAngle * (Math.PI / 180)); + var y = + startY + + (_self.layerDistance * radius + noise) * + Math.sin(currentAngle * (Math.PI / 180)); + currentAngle += (shift * o.radius) / 2; + var posData = _self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / _self.intSteps) * (x - o.x); + posData.ydistance = (1.0 / _self.intSteps) * (y - o.y); + o.layoutData = posData; + }); + } else { + var currentAngle = 0; + var shift = 360 / currentOrbit.length; + currentOrbit.forEach(function (o) { + var x = + startX + + _self.layerDistance * + radius * + Math.cos(currentAngle * (Math.PI / 180)); + var y = + startY + + _self.layerDistance * + radius * + Math.sin(currentAngle * (Math.PI / 180)); + var posData = _self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / _self.intSteps) * (x - o.x); + posData.ydistance = (1.0 / _self.intSteps) * (y - o.y); + o.layoutData = posData; + currentAngle += shift; + }); + } + }; + var ConcentricLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.defaults = { + startAngle: Math.PI, + clockwise: true, + equidistant: false, + avoidOverlap: true, + minNodeSpacing: 10, + maxNodeSize: 50, + levelWidth: 1, + }; + this.boolTransition = true; + this.intSteps = 50; + this.inited = false; + }; + ConcentricLayout.prototype.getConfig = function () { + return [ + { label: "节点大小", maxNodeSize: 50 }, + { label: "分层系数", levelWidth: 1 }, + ]; + }; + ConcentricLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.defaults.maxNodeSize = Number(layoutConfig["maxNodeSize"]) || 50; + this.defaults.levelWidth = Number(layoutConfig["levelWidth"]) || 1; + } + this.initAlgo(); + }; + ConcentricLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + ConcentricLayout.prototype.initAlgo = function () { + var self = this; + let options = self.defaults; + let nodes = self.nodes; + var bb = { + x1: 0, + y1: 0, + w: options.maxNodeSize * 5, + h: options.maxNodeSize * 5, + }; + let center = { x: bb.x1 + bb.w / 2, y: bb.y1 + bb.h / 2 }; + let nodeValues = []; + for (let i = 0; i < nodes.length; i++) { + let node = nodes[i]; + let value; + value = (node.inLinks || []).length + (node.outLinks || []).length; + nodeValues.push({ value: value, node: node }); + } + nodeValues.sort(function (a, b) { + return b.value - a.value; + }); + let levelWidth = options.levelWidth; + let levels = [[]]; + let currentLevel = levels[0]; + for (let i = 0; i < nodeValues.length; i++) { + let val = nodeValues[i]; + if (currentLevel.length > 0) { + let diff = Math.abs(currentLevel[0].value - val.value); + if (diff >= levelWidth) { + currentLevel = []; + levels.push(currentLevel); + } + } + currentLevel.push(val); + } + let minDist = options.maxNodeSize + options.minNodeSpacing; + if (options.avoidOverlap) { + let firstLvlHasMulti = levels.length > 0 && levels[0].length > 1; + let maxR = Math.min(bb.w, bb.h) / 2 - minDist; + let rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0); + minDist = Math.min(minDist, rStep); + } + let r = 0; + for (let i = 0; i < levels.length; i++) { + let level = levels[i]; + let sweep = 2 * Math.PI - (2 * Math.PI) / level.length; + let dTheta = (level.dTheta = sweep / Math.max(1, level.length - 1)); + if (level.length > 1 && options.avoidOverlap) { + let dcos = Math.cos(dTheta) - Math.cos(0); + let dsin = Math.sin(dTheta) - Math.sin(0); + let rMin = Math.sqrt((minDist * minDist) / (dcos * dcos + dsin * dsin)); + r = Math.max(rMin, r); + } + level.r = r; + r += minDist; + } + if (options.equidistant) { + let rDeltaMax = 0; + let r = 0; + for (let i = 0; i < levels.length; i++) { + let level = levels[i]; + let rDelta = level.r - r; + rDeltaMax = Math.max(rDeltaMax, rDelta); + } + r = 0; + for (let i = 0; i < levels.length; i++) { + let level = levels[i]; + if (i === 0) { + r = level.r; + } + level.r = r; + r += rDeltaMax; + } + } + for (let i = 0; i < levels.length; i++) { + let level = levels[i]; + let dTheta = level.dTheta; + let r = level.r; + for (let j = 0; j < level.length; j++) { + let val = level[j]; + let theta = options.startAngle + (self.clockwise ? 1 : -1) * dTheta * j; + var x = center.x + r * Math.cos(theta); + var y = center.y + r * Math.sin(theta); + var posData = self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / self.intSteps) * (x - val.node.x); + posData.ydistance = (1.0 / self.intSteps) * (y - val.node.y); + val.node.layoutData = posData; + } + } + this.inited = true; + }; + ConcentricLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + ConcentricLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var RotateLayout = function (nodes, links, angle) { + this.nodes = nodes; + this.links = links; + this.angle = angle || 10; + this.direction = "sn"; + this.boolTransition = true; + this.intSteps = 30; + this.inited = false; + }; + RotateLayout.prototype.getConfig = function () { + return [ + { label: "旋转角度", angle: 10 }, + { + label: "旋转方向", + direction: [ + { label: "顺时针", value: "sn" }, + { label: "逆时针", value: "ns" }, + ], + }, + ]; + }; + RotateLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.angle = Number(layoutConfig["angle"]) || 10; + this.direction = layoutConfig["direction"] || "sn"; + } + if (this.direction == "ns") { + this.angle = Math.abs(this.angle); + } else { + this.angle = -Math.abs(this.angle); + } + this.initAlgo(); + }; + RotateLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + RotateLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + RotateLayout.prototype.initAlgo = function () { + var self = this; + var sin = Math.sin((-self.angle * Math.PI) / 180); + var cos = Math.cos((-self.angle * Math.PI) / 180); + var px = 0; + var py = 0; + self.nodes.forEach(function (n) { + var dx = n.x - px; + var dy = n.y - py; + var tempX = px + dx * cos - dy * sin; + var tempY = py + dy * cos + dx * sin; + var posData = self.newLayoutData(); + posData.finishx = tempX; + posData.finishy = tempY; + posData.xdistance = (1.0 / self.intSteps) * (tempX - n.x); + posData.ydistance = (1.0 / self.intSteps) * (tempY - n.y); + n.layoutData = posData; + }); + this.inited = true; + }; + RotateLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var ScaleLayout = function (nodes, links, scale) { + this.nodes = nodes; + this.links = links; + this.scale = scale || 1; + this.boolTransition = true; + this.intSteps = 30; + this.inited = false; + }; + ScaleLayout.prototype.getConfig = function () { + return [{ label: "缩放比例", scale: 1.1 }]; + }; + ScaleLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.scale = Number(layoutConfig["scale"]) || 1.1; + } + this.initAlgo(); + }; + ScaleLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + ScaleLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + ScaleLayout.prototype.initAlgo = function () { + var self = this; + var nodeCount = self.nodes.length; + var xMean = 0, + yMean = 0; + this.inited = true; + self.nodes.forEach(function (n) { + xMean += n.x; + yMean += n.y; + }); + xMean /= nodeCount; + yMean /= nodeCount; + self.nodes.forEach(function (n) { + var dx = (n.x - xMean) * self.scale; + var dy = (n.y - yMean) * self.scale; + var tempX = xMean + dx; + var tempY = yMean + dy; + var posData = self.newLayoutData(); + posData.finishx = tempX; + posData.finishy = tempY; + posData.xdistance = (1.0 / self.intSteps) * (tempX - n.x); + posData.ydistance = (1.0 / self.intSteps) * (tempY - n.y); + n.layoutData = posData; + }); + }; + ScaleLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var SpringLayout2 = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.stretch = 0.5; + this.repulsion_range_sq = 2000; + this.force_multiplier = 0.02; + this.inited = false; + }; + SpringLayout2.prototype.newLayoutData = function () { + var layoutData = { + edgedx: 0.0, + edgedy: 0.0, + repulsiondx: 0.0, + repulsiondy: 0.0, + dx: 0.0, + dy: 0.0, + }; + return layoutData; + }; + SpringLayout2.prototype.getConfig = function () { + return [ + { label: "区域大小", repulsion: 1000000 }, + { label: "边长度", stretch: 0.015 }, + { label: "收敛系数", force: 10.0 }, + ]; + }; + SpringLayout2.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.stretch = Number(layoutConfig["stretch"]) || 0.015; + this.repulsion_range_sq = Number(layoutConfig["repulsion"]) || 1000000; + this.force_multiplier = Number(layoutConfig["force"]) || 10; + } + this.initAlgo(); + }; + SpringLayout2.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + SpringLayout2.prototype.initAlgo = function () { + var _self = this; + _self.nodes.forEach(function (n) { + n.layoutData = _self.newLayoutData(); + }); + this.inited = true; + }; + SpringLayout2.prototype.goAlgo = function () { + var _self = this; + _self.nodes.forEach(function (n) { + var svd = n.layoutData; + svd.dx /= 4; + svd.dy /= 4; + svd.edgedx = svd.edgedy = 0; + svd.repulsiondx = svd.repulsiondy = 0; + }); + _self.relaxEdges(); + _self.calculateRepulsion(); + _self.moveNodes(); + }; + SpringLayout2.prototype.relaxEdges = function () { + var _self = this; + _self.links.forEach(function (link) { + var node1 = link.source; + var node2 = link.target; + var vx = node1.x - node2.x; + var vy = node1.y - node2.y; + var len = Math.sqrt(vx * vx + vy * vy); + len = len == 0 ? 0.0001 : len; + var f = (_self.force_multiplier * (1 - len)) / len; + f = f * Math.pow(_self.stretch, 2); + var dx = f * vx; + var dy = f * vy; + var v1D = node1.layoutData; + var v2D = node2.layoutData; + v1D.edgedx += dx; + v1D.edgedy += dy; + v2D.edgedx += -dx; + v2D.edgedy += -dy; + }); + }; + SpringLayout2.prototype.calculateRepulsion = function () { + var _self = this; + _self.nodes.forEach(function (node) { + var dx = 0, + dy = 0; + _self.nodes.forEach(function (n) { + if (node.id != n.id) { + var vx = node.x - n.x; + var vy = node.y - n.y; + var distanceSq = vx * vx + vy * vy; + if (distanceSq == 0) { + dx += Math.random(); + dy += Math.random(); + } else if (distanceSq < _self.repulsion_range_sq) { + var factor = 1; + dx += (factor * vx) / distanceSq; + dy += (factor * vy) / distanceSq; + } + } + }); + var dlen = dx * dx + dy * dy; + if (dlen > 0) { + dlen = Math.sqrt(dlen) / 2; + var layoutData = node.layoutData; + layoutData.repulsiondx += dx / dlen; + layoutData.repulsiondy += dy / dlen; + } + }); + }; + SpringLayout2.prototype.moveNodes = function () { + var _self = this; + _self.nodes.forEach(function (node) { + var vd = node.layoutData; + vd.dx += vd.repulsiondx + vd.edgedx; + vd.dy += vd.repulsiondy + vd.edgedy; + node.x += Math.max(-5, Math.min(5, vd.dx)); + node.y += Math.max(-5, Math.min(5, vd.dy)); + }); + }; + var SphereLayout = function (nodes, links, radius) { + this.nodes = nodes; + this.links = links; + this.radius = radius || 800; + this.boolTransition = true; + this.intSteps = 30; + this.inited = false; + }; + SphereLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + SphereLayout.prototype.getConfig = function () { + return [{ label: "半径", radius: 500 }]; + }; + SphereLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.radius = Number(layoutConfig["radius"]) || 500; + } + this.initAlgo(); + }; + SphereLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + SphereLayout.prototype.initAlgo = function () { + var self = this; + var nodeCount = self.nodes.length; + this.inited = true; + var area = 0; + self.nodes.forEach(function (n, i) { + var phi = Math.acos(-1 + (2 * i) / nodeCount); + var theta = Math.sqrt(nodeCount * Math.PI) * phi; + var sinPhiRadius = Math.sin(phi) * self.radius; + var tempX = sinPhiRadius * Math.sin(theta); + var tempY = Math.cos(phi) * self.radius; + var posData = self.newLayoutData(); + posData.finishx = tempX; + posData.finishy = tempY; + posData.xdistance = (1.0 / self.intSteps) * (tempX - n.x); + posData.ydistance = (1.0 / self.intSteps) * (tempY - n.y); + n.layoutData = posData; + }); + }; + SphereLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var TreeLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.nodeIds = []; + this.nodeNeighbers = []; + this.distX = 80; + this.distY = 80; + this.currentX = 0; + this.currentY = 0; + this.direction = "UD"; + this.boolTransition = true; + this.intSteps = 50; + this.hasCycle = false; + this.inited = false; + }; + TreeLayout.prototype.getConfig = function () { + return [ + { label: "点间距", distX: 80 }, + { label: "层间距", distY: 120 }, + { + label: "排列方向", + direction: [ + { label: "上下", value: "UD" }, + { label: "下上", value: "DU" }, + { label: "左右", value: "LR" }, + { label: "右左", value: "RL" }, + ], + }, + ]; + }; + TreeLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.distX = Number(layoutConfig["distX"]) || 100; + this.distY = Number(layoutConfig["distY"]) || 120; + this.direction = layoutConfig["direction"] || "UD"; + } + this.nodeIds = []; + this.nodeNeighbers = []; + this.initAlgo(); + }; + TreeLayout.prototype.initAlgo = function () { + var _self = this; + _self.nodes.forEach(function (node) { + _self.checkHasCycle(node, []); + _self.nodeIds.push(node.id); + var neighbers = _self.initNodeNeighbers(node); + _self.nodeNeighbers.push(neighbers); + }); + _self.buildTree(); + this.inited = true; + }; + TreeLayout.prototype.initNodeNeighbers = function (node) { + var _self = this; + var nodeNeighbers = []; + var outLinks = node.outLinks || []; + outLinks.forEach(function (link) { + var target = link.target; + var source = link.source; + if (source.id != target.id && source.visible && target.visible) { + var index = _self.nodeIds.indexOf(target.id); + var childNodes = _self.nodeNeighbers[index] || []; + var childNodeIds = []; + childNodes.forEach(function (n) { + childNodeIds.push(n.id); + }); + if (childNodeIds.indexOf(node.id) == -1) { + nodeNeighbers.push(target); + } + } + }); + return nodeNeighbers; + }; + TreeLayout.prototype.runLayout = function () { + if (!this.hasCycle && this.inited) { + this.goAlgo(); + } + }; + TreeLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + TreeLayout.prototype.buildTree = function () { + var _self = this; + var roots = _self.getRoots(); + if (roots.length > 0) { + _self.calculateRootsX(roots); + roots.forEach(function (node) { + _self.calculateNodeX(node); + _self.currentX += node.sizeT / 2 + _self.distX; + _self.buildNodeTree(node, _self.currentX); + }); + } + }; + TreeLayout.prototype.getRoots = function () { + var _self = this; + var roots = []; + _self.nodes.forEach(function (node) { + if ((node.inLinks || []).length == 0) { + roots.push(node); + } + }); + return roots; + }; + TreeLayout.prototype.calculateRootsX = function (roots) { + var _self = this; + var size = 0; + roots.forEach(function (node) { + var childNodes = _self.getSuccessors(node); + var childrenNum = childNodes.length; + if (childrenNum != 0) { + childNodes.forEach(function (node) { + size += _self.calculateNodeX(node) + _self.distX; + }); + } + size = Math.max(0, size - _self.distX); + node.sizeT = size; + }); + return size; + }; + TreeLayout.prototype.calculateNodeX = function (node) { + var _self = this; + var size = 0; + var childNodes = _self.getSuccessors(node); + var childrenNum = childNodes.length; + if (childrenNum != 0) { + childNodes.forEach(function (node) { + size += _self.calculateNodeX(node) + _self.distX; + }); + } + size = Math.max(0, size - _self.distX); + node.sizeT = size; + return size; + }; + TreeLayout.prototype.buildNodeTree = function (node, x) { + var _self = this; + _self.currentY += _self.distY; + _self.currentX = x; + _self.setCurrentPositionFor(node); + var sizeXofCurrent = node.sizeT; + var lastX = x - sizeXofCurrent / 2; + var sizeXofChild; + var startXofChild; + var childNodes = _self.getSuccessors(node); + childNodes.forEach(function (n) { + sizeXofChild = n.sizeT; + startXofChild = lastX + sizeXofChild / 2; + _self.buildNodeTree(n, startXofChild); + lastX = lastX + sizeXofChild + _self.distX; + }); + _self.currentY -= _self.distY; + }; + TreeLayout.prototype.setCurrentPositionFor = function (node) { + var _self = this; + var x = _self.currentX; + var y = _self.currentY; + var tempx = x; + if (_self.direction == "DU") { + y = -y; + } else if (_self.direction == "LR") { + x = y; + y = tempx; + } else if (_self.direction == "RL") { + x = -y; + y = tempx; + } + var posData = _self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / _self.intSteps) * (x - node.x); + posData.ydistance = (1.0 / _self.intSteps) * (y - node.y); + node.layoutData = posData; + }; + TreeLayout.prototype.getSuccessors = function (node) { + var _self = this; + var index = _self.nodeIds.indexOf(node.id); + var childNodes = _self.nodeNeighbers[index] || []; + return childNodes; + }; + TreeLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + TreeLayout.prototype.checkHasCycle = function (node, pathNodes) { + var _self = this; + (node.outLinks || []).forEach(function (_link) { + var target = _link.target; + if (node.id == target.id || pathNodes.indexOf(target.id) != -1) { + _self.hasCycle = true; + return; + } + pathNodes.push(target.id); + _self.checkHasCycle(target, pathNodes); + }); + }; + var BalloonLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.nodeIds = []; + this.nodeNeighbers = []; + this.distX = 50; + this.distY = 50; + this.currentX = 0; + this.currentY = 0; + this.radius = 1000; + this.boolTransition = true; + this.intSteps = 50; + this.hasCycle = false; + this.inited = false; + }; + BalloonLayout.prototype.getConfig = function () { + return [{ label: "区域大小", radius: 1000 }]; + }; + BalloonLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.radius = Number(layoutConfig["radius"]) || 1000; + } + this.initAlgo(); + }; + BalloonLayout.prototype.runLayout = function () { + if (!this.hasCycle && this.inited) { + this.goAlgo(); + } + }; + BalloonLayout.prototype.initAlgo = function () { + var _self = this; + this.inited = true; + _self.nodeIds = []; + _self.nodeNeighbers = []; + _self.nodes.forEach(function (node) { + _self.nodeIds.push(node.id); + var neighbers = _self.initNodeNeighbers(node); + _self.nodeNeighbers.push(neighbers); + _self.checkHasCycle(node, []); + }); + _self.buildTree(); + _self.setRootPolars(); + }; + BalloonLayout.prototype.initNodeNeighbers = function (node) { + var _self = this; + var nodeNeighbers = []; + var outLinks = node.outLinks || []; + outLinks.forEach(function (link) { + var target = link.target; + var source = link.source; + if (source.id != target.id && source.visible && target.visible) { + var index = _self.nodeIds.indexOf(target.id); + var childNodes = _self.nodeNeighbers[index] || []; + var childNodeIds = []; + childNodes.forEach(function (n) { + childNodeIds.push(n.id); + }); + if (childNodeIds.indexOf(node.id) == -1) { + nodeNeighbers.push(target); + } + } + }); + return nodeNeighbers; + }; + BalloonLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + BalloonLayout.prototype.setRootPolars = function () { + var _self = this; + var roots = _self.getRoots(); + var center = _self.getCenter(); + _self.setPolars(roots, center, _self.radius); + }; + BalloonLayout.prototype.setRootPolar = function (root) { + root.x = 10; + root.y = 10; + }; + BalloonLayout.prototype.setPolars = function ( + kids, + parentLocation, + parentRadius, + ) { + var _self = this; + var childCount = kids.length; + if (childCount == 0) { + return; + } + var angle = Math.max(0, (Math.PI / 2) * (1 - 2.0 / childCount)); + var childRadius = (parentRadius * Math.cos(angle)) / (1 + Math.cos(angle)); + var radius = parentRadius - childRadius; + var rand = Math.random(); + for (var i = 0; i < childCount; i++) { + var node = kids[i]; + var theta = (i * 2 * Math.PI) / childCount + rand; + var x = radius * Math.cos(theta); + var y = radius * Math.sin(theta); + x = x + parentLocation.x; + y = y + parentLocation.y; + var posData = _self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / _self.intSteps) * (x - node.x); + posData.ydistance = (1.0 / _self.intSteps) * (y - node.y); + node.layoutData = posData; + var p = { x: x, y: y }; + var childNodes = _self.getSuccessors(node); + _self.setPolars(childNodes, p, childRadius); + } + }; + BalloonLayout.prototype.getCenter = function (node) { + var _self = this; + var parent = _self.getParent(node); + if (parent == null) { + return _self.getCenter(); + } + return { x: parent.x, y: parent.y }; + }; + BalloonLayout.prototype.getCenter = function () { + var _self = this; + return { x: 0, y: 0 }; + }; + BalloonLayout.prototype.getParent = function (node) { + var inLinks = node.inLinks || []; + if (inLinks.length > 0) { + return inLinks[0].source; + } + return null; + }; + BalloonLayout.prototype.buildTree = function () { + var _self = this; + var roots = _self.getRoots(); + if (roots.length > 0) { + _self.calculateRootsX(roots); + roots.forEach(function (node) { + _self.calculateNodeX(node); + _self.currentX += node.sizeT / 2 + _self.distX; + _self.buildNodeTree(node, _self.currentX); + }); + } + }; + BalloonLayout.prototype.getRoots = function () { + var _self = this; + var roots = []; + _self.nodes.forEach(function (node) { + if ((node.inLinks || []).length == 0) { + roots.push(node); + } + }); + return roots; + }; + BalloonLayout.prototype.calculateRootsX = function (roots) { + var _self = this; + var size = 0; + roots.forEach(function (node) { + var childNodes = _self.getSuccessors(node); + var childrenNum = childNodes.length; + if (childrenNum != 0) { + childNodes.forEach(function (node) { + size += _self.calculateNodeX(node) + _self.distX; + }); + } + size = Math.max(0, size - _self.distX); + node.sizeT = size; + }); + return size; + }; + BalloonLayout.prototype.calculateNodeX = function (node) { + var _self = this; + var size = 0; + var childNodes = _self.getSuccessors(node); + var childrenNum = childNodes.length; + if (childrenNum != 0) { + childNodes.forEach(function (node) { + size += _self.calculateNodeX(node) + _self.distX; + }); + } + size = Math.max(0, size - _self.distX); + node.sizeT = size; + return size; + }; + BalloonLayout.prototype.buildNodeTree = function (node, x) { + var _self = this; + _self.currentY += _self.distY; + _self.currentX = x; + _self.setCurrentPositionFor(node); + var sizeXofCurrent = node.sizeT; + var lastX = x - sizeXofCurrent / 2; + var sizeXofChild; + var startXofChild; + var childNodes = _self.getSuccessors(node); + childNodes.forEach(function (n) { + sizeXofChild = n.sizeT; + startXofChild = lastX + sizeXofChild / 2; + _self.buildNodeTree(n, startXofChild); + lastX = lastX + sizeXofChild + _self.distX; + }); + _self.currentY -= _self.distY; + }; + BalloonLayout.prototype.setCurrentPositionFor = function (node) { + var _self = this; + var x = _self.currentX; + var y = _self.currentY; + node.tempx = x; + node.tempy = y; + }; + BalloonLayout.prototype.getSuccessors = function (node) { + var _self = this; + var index = _self.nodeIds.indexOf(node.id); + var childNodes = _self.nodeNeighbers[index] || []; + return childNodes; + }; + BalloonLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + BalloonLayout.prototype.checkHasCycle = function (node, pathNodes) { + var _self = this; + (node.outLinks || []).forEach(function (_link) { + var target = _link.target; + if (node.id == target.id || pathNodes.indexOf(target.id) != -1) { + _self.hasCycle = true; + return; + } + pathNodes.push(target.id); + _self.checkHasCycle(target, pathNodes); + }); + }; + var RadiaTreeLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.nodeIds = []; + this.nodeNeighbers = []; + this.distX = 50; + this.distY = 50; + this.currentX = 0; + this.currentY = 0; + this.boolTransition = true; + this.intSteps = 50; + this.hasCycle = false; + this.inited = false; + }; + RadiaTreeLayout.prototype.getConfig = function () { + return [ + { label: "水平间距", distX: 50 }, + { label: "垂直间距", distY: 50 }, + ]; + }; + RadiaTreeLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.distX = Number(layoutConfig["distX"]) || 50; + this.distY = Number(layoutConfig["distY"]) || 50; + } + this.initAlgo(); + }; + RadiaTreeLayout.prototype.runLayout = function () { + if (!this.hasCycle && this.inited) { + this.goAlgo(); + } + }; + RadiaTreeLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + RadiaTreeLayout.prototype.initAlgo = function () { + var _self = this; + this.inited = true; + _self.nodeIds = []; + _self.nodeNeighbers = []; + _self.nodes.forEach(function (node) { + _self.nodeIds.push(node.id); + var neighbers = _self.initNodeNeighbers(node); + _self.nodeNeighbers.push(neighbers); + _self.checkHasCycle(node, []); + }); + _self.buildTree(); + _self.setRadialLocations(); + }; + RadiaTreeLayout.prototype.checkHasCycle = function (node, pathNodes) { + var _self = this; + (node.outLinks || []).forEach(function (_link) { + var target = _link.target; + if (node.id == target.id || pathNodes.indexOf(target.id) != -1) { + _self.hasCycle = true; + return; + } + pathNodes.push(target.id); + _self.checkHasCycle(target, pathNodes); + }); + }; + RadiaTreeLayout.prototype.initNodeNeighbers = function (node) { + var _self = this; + var nodeNeighbers = []; + var outLinks = node.outLinks || []; + outLinks.forEach(function (link) { + var target = link.target; + if (node.id != target.id && target.visible) { + var index = _self.nodeIds.indexOf(target.id); + var childNodes = _self.nodeNeighbers[index] || []; + var childNodeIds = []; + childNodes.forEach(function (n) { + childNodeIds.push(n.id); + }); + if (childNodeIds.indexOf(node.id) == -1) { + nodeNeighbers.push(target); + } + } + }); + return nodeNeighbers; + }; + RadiaTreeLayout.prototype.buildTree = function () { + var _self = this; + var roots = _self.getRoots(); + if (roots.length > 0) { + _self.calculateRootsX(roots); + roots.forEach(function (node) { + _self.calculateNodeX(node); + _self.currentX += node.sizeT / 2 + _self.distX; + _self.buildNodeTree(node, _self.currentX); + }); + } + }; + RadiaTreeLayout.prototype.getRoots = function () { + var _self = this; + var roots = []; + _self.nodes.forEach(function (node) { + if ((node.inLinks || []).length == 0) { + roots.push(node); + } + }); + return roots; + }; + RadiaTreeLayout.prototype.calculateRootsX = function (roots) { + var _self = this; + var size = 0; + roots.forEach(function (node) { + var childNodes = _self.getSuccessors(node); + var childrenNum = childNodes.length; + if (childrenNum != 0) { + childNodes.forEach(function (node) { + size += _self.calculateNodeX(node) + _self.distX; + }); + } + size = Math.max(0, size - _self.distX); + node.sizeT = size; + }); + return size; + }; + RadiaTreeLayout.prototype.calculateNodeX = function (node) { + var _self = this; + var size = 0; + var childNodes = _self.getSuccessors(node); + if (childNodes.length != 0) { + childNodes.forEach(function (_node) { + size += _self.calculateNodeX(_node) + _self.distX; + }); + } + size = Math.max(0, size - _self.distX); + node.sizeT = size; + return size; + }; + RadiaTreeLayout.prototype.buildNodeTree = function (node, x) { + var _self = this; + _self.currentY += _self.distY; + _self.currentX = x; + _self.setCurrentPositionFor(node); + var sizeXofCurrent = node.sizeT; + var lastX = x - sizeXofCurrent / 2; + var sizeXofChild; + var startXofChild; + var childNodes = _self.getSuccessors(node); + childNodes.forEach(function (n) { + sizeXofChild = n.sizeT; + startXofChild = lastX + sizeXofChild / 2; + _self.buildNodeTree(n, startXofChild); + lastX = lastX + sizeXofChild + _self.distX; + }); + _self.currentY -= _self.distY; + }; + RadiaTreeLayout.prototype.setCurrentPositionFor = function (node) { + var _self = this; + var x = _self.currentX; + var y = _self.currentY; + node.tempx = x; + node.tempy = y; + }; + RadiaTreeLayout.prototype.getSuccessors = function (node) { + var _self = this; + var index = _self.nodeIds.indexOf(node.id); + var childNodes = _self.nodeNeighbers[index] || []; + return childNodes; + }; + RadiaTreeLayout.prototype.setRadialLocations = function () { + var _self = this; + var maxPoint = _self.getMaxXY(); + var maxx = maxPoint.x; + var maxy = maxPoint.y; + var theta = (2 * Math.PI) / maxx; + var deltaRadius = maxx / 2 / maxy; + _self.nodes.forEach(function (node) { + var _theta = node.tempx * theta; + var _raduis = (node.tempy - _self.distY) * deltaRadius; + var x = _raduis * Math.cos(_theta); + var y = _raduis * Math.sin(_theta); + var posData = _self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / _self.intSteps) * (x - node.x); + posData.ydistance = (1.0 / _self.intSteps) * (y - node.y); + node.layoutData = posData; + }); + }; + RadiaTreeLayout.prototype.getMaxXY = function () { + var _self = this; + var maxx = 0, + maxy = 0; + _self.nodes.forEach(function (node) { + if (!node.tempx) { + node.tempx = _self.currentX; + } + if (!node.tempy) { + node.tempy = _self.currentY; + } + maxx = Math.max(maxx, node.tempx); + maxy = Math.max(maxy, node.tempy); + }); + return { x: maxx, y: maxy }; + }; + RadiaTreeLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var TopoTreeLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.raduis = 50; + this.boolTransition = true; + this.intSteps = 50; + this.hasCycle = false; + this.inited = false; + }; + TopoTreeLayout.prototype.getConfig = function () { + return [{ label: "节点大小", raduis: 50 }]; + }; + TopoTreeLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.raduis = Number(layoutConfig["raduis"]) || 50; + } + this.initAlgo(); + }; + TopoTreeLayout.prototype.initAlgo = function () { + var _self = this; + var roots = _self.getRoots(); + roots[0].tempX = roots[0].x = 1000; + roots[0].tempY = roots[0].y = 500; + _self.countRadius(roots[0], _self.raduis); + _self.layout(roots[0], _self.raduis * 3); + this.inited = true; + }; + TopoTreeLayout.prototype.countRadius = function (root, minR) { + var self = this; + minR = minR == null ? self.raduis : minR; + var children = self.getSuccessors(root); + var size = children.length; + if (size <= 1) { + root.rdegree = 0; + root.mradius = minR; + root.totalRadius = minR; + } + children.forEach(function (child) { + self.countRadius(child, minR); + }); + if (size > 1) { + var child0 = children[0]; + var totalRadius = child0.totalRadius; + if (size == 2) { + size = 3; + } + var degree = Math.PI / size; + var pRadius = totalRadius / Math.sin(degree); + root.mradius = pRadius; + root.totalRadius = pRadius + totalRadius; + root.rdegree = degree * 2; + } + }; + TopoTreeLayout.prototype.layout = function (root, minR) { + var self = this; + var children = self.getSuccessors(root); + var len = children.length; + var degree = root.rdegree; + var r = root.mradius; + var rootPosition = { x: root.tempX, y: root.tempY }; + children.forEach(function (node, index) { + var s = Math.sin(degree * index), + c = Math.cos(degree * index), + x = s * r, + y = c * r; + x = Math.round(x + rootPosition.x); + y = Math.round(y + rootPosition.y); + node.tempX = x; + node.tempY = y; + var posData = self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / self.intSteps) * (x - node.x); + posData.ydistance = (1.0 / self.intSteps) * (y - node.y); + node.layoutData = posData; + self.layout(node, minR); + }); + }; + TopoTreeLayout.prototype.runLayout = function () { + if (!this.hasCycle && this.inited) { + this.goAlgo(); + } + }; + TopoTreeLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + TopoTreeLayout.prototype.getRoots = function () { + var _self = this; + var roots = []; + _self.nodes.forEach(function (node) { + if ((node.inLinks || []).length == 0) { + roots.push(node); + } + _self.checkHasCycle(node, []); + }); + return roots; + }; + TopoTreeLayout.prototype.checkHasCycle = function (node, pathNodes) { + var _self = this; + (node.outLinks || []).forEach(function (_link) { + var target = _link.target; + if (node.id == target.id || pathNodes.indexOf(target.id) != -1) { + _self.hasCycle = true; + return; + } + pathNodes.push(target.id); + _self.checkHasCycle(target, pathNodes); + }); + }; + TopoTreeLayout.prototype.getSuccessors = function (node) { + var _self = this; + var children = []; + if (!node) { + return children; + } + (node.outLinks || []).forEach(function (l) { + children.push(l.target); + }); + return children; + }; + TopoTreeLayout.prototype.goAlgo = function () { + var _self = this; + var position = null; + var nodes = _self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (_self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + TopoTreeLayout.prototype.checkHasCycle = function (node, pathNodes) { + var _self = this; + (node.outLinks || []).forEach(function (_link) { + var target = _link.target; + if (node.id == target.id || pathNodes.indexOf(target.id) != -1) { + _self.hasCycle = true; + return; + } + pathNodes.push(target.id); + _self.checkHasCycle(target, pathNodes); + }); + }; + var ForceD3Layout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.size = [1400, 700]; + this.alpha = 0.9; + this.friction = 0.9; + this.linkDistance = 120; + this.linkStrength = 0.09; + this.charge = -200; + this.gravity = 0.015; + this.theta = 0.8; + this.distances = []; + this.strengths = []; + this.charges = []; + this.noverlap = false; + this.inited = false; + }; + ForceD3Layout.prototype.getConfig = function () { + return [ + { label: "斥力", froce: 0.95 }, + { label: "边长度", linkDistance: 150 }, + { label: "边强度", linkStrength: 0.09 }, + { label: "引力", charge: -300 }, + { label: "重力", gravity: 0.015 }, + { + label: "避免重叠", + noverlap: [ + { label: "否", value: "false" }, + { label: "是", value: "true" }, + ], + }, + ]; + }; + ForceD3Layout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.friction = Number(layoutConfig["froce"]) || 0.95; + this.linkDistance = Number(layoutConfig["linkDistance"]) || 150; + this.linkStrength = Number(layoutConfig["linkStrength"]) || 0.09; + this.charge = Number(layoutConfig["charge"]) || -300; + this.gravity = Number(layoutConfig["gravity"]) || 0.015; + this.noverlap = layoutConfig["noverlap"] || "false"; + this.size = layoutConfig["size"] || [1400, 700]; + this.initAlgo(); + } + }; + ForceD3Layout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + ForceD3Layout.prototype.initAlgo = function () { + var _self = this; + var i, + j, + n = _self.nodes.length, + m = _self.links.length, + w = _self.size[0], + h = _self.size[1], + neighbors = [], + o; + _self.charges = []; + for (i = 0; i < n; ++i) { + (o = _self.nodes[i]).index = i; + o.weight = 0; + o.px = o.x; + o.py = o.y; + _self.charges[i] = _self.charge; + } + for (i = 0; i < m; ++i) { + o = _self.links[i]; + var source = o.source; + var target = o.target; + _self.distances[i] = _self.linkDistance; + _self.strengths[i] = _self.linkStrength; + ++source.weight; + ++target.weight; + } + this.inited = true; + function position(dimension, size) { + var neighbors = neighbor(i), + j = -1, + m = neighbors.length, + x; + while (++j < m) { + if (!isNaN((x = neighbors[j][dimension]))) { + return x; + } + } + return Math.random() * size; + } + function neighbor() { + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = _self.links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + return neighbors[i]; + } + return this.resume(); + }; + ForceD3Layout.prototype.goAlgo = function () { + this.tick(); + }; + ForceD3Layout.prototype.alpha1 = function (x) { + var _self = this; + if (!arguments.length) { + return _self.alpha; + } + if (_self.alpha) { + if (x > 0) { + _self.alpha = x; + } else { + _self.alpha = 0; + } + } else if (x > 0) { + _self.tick(); + } + return this; + }; + ForceD3Layout.prototype.resume = function () { + return this.alpha1(0.8); + }; + ForceD3Layout.prototype.stop = function () { + return this.alpha1(0); + }; + ForceD3Layout.prototype.tick = function () { + var _self = this; + if ((_self.alpha *= 0.99) < 0.0001) { + _self.alpha = 0.01; + return; + } + var n = _self.nodes.length, + m = _self.links.length, + q, + i, + o, + s, + t, + l, + k, + x, + y; + for (i = 0; i < m; ++i) { + o = _self.links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if ((l = x * x + y * y)) { + l = + (_self.alpha * + _self.strengths[i] * + ((l = Math.sqrt(l)) - _self.distances[i])) / + l; + x *= l; + y *= l; + t.x -= x * (k = s.weight / (t.weight + s.weight)); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if ((k = _self.alpha * _self.gravity)) { + x = _self.size[0] / 2; + y = _self.size[1] / 2; + i = -1; + if (k) { + while (++i < n) { + o = _self.nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + } + if (_self.charge) { + _self.forceAccumulate( + (q = _self.quadtree(_self.nodes)), + _self.alpha, + _self.charges, + ); + i = -1; + while (++i < n) { + if (!(o = _self.nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = _self.nodes[i]; + if (o.fixed) { + if (!o.isDragging) { + o.x = o.px; + o.y = o.py; + } else { + o.px = o.x; + o.py = o.y; + } + } else { + o.x -= (o.px - (o.px = o.x)) * _self.friction; + o.y -= (o.py - (o.py = o.y)) * _self.friction; + if (_self.noverlap == "true") { + q.visit(collide(o)); + } + } + } + function repulse(node) { + return function (quad, x1, y1, x2, y2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, + dy = quad.cy - node.y, + dn = 1 / Math.sqrt(dx * dx + dy * dy); + if ((x2 - x1) * dn < _self.theta) { + var k = quad.charge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + return true; + } + if (quad.point && isFinite(dn)) { + var k = quad.pointCharge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + function collide(node) { + var r = node.radius * node.scaleX + 1, + nx1 = node.x - r, + nx2 = node.x + r, + ny1 = node.y - r, + ny2 = node.y + r; + return function (quad, x1, y1, x2, y2) { + if (quad.point && quad.point !== node) { + var x = node.x - quad.point.x, + y = node.y - quad.point.y, + l = Math.sqrt(x * x + y * y), + r = node.radius * node.scaleX + quad.point.radius; + if (l < r) { + l = ((l - r) / l) * 0.5; + node.x -= x *= l; + node.y -= y *= l; + quad.point.x += x; + quad.point.y += y; + } + } + return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; + }; + } + }; + ForceD3Layout.prototype.forceAccumulate = function (quad, alpha, charges) { + var _self = this; + var cx = 0, + cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, + n = nodes.length, + i = -1, + c; + while (++i < n) { + c = nodes[i]; + if (c == null) { + continue; + } + _self.forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - 0.5; + quad.point.y += Math.random() - 0.5; + } + var k = _self.alpha * _self.charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + }; + ForceD3Layout.prototype.quadtree = function (points, x1, y1, x2, y2) { + var _self = this; + var p, + i = -1, + n = points.length; + if (n && isNaN(points[0].x)) { + points = points.map(function (p) { + return { x: p[0], y: p[1] }; + }); + } + if (arguments.length < 5) { + if (arguments.length === 3) { + y2 = x2 = y1; + y1 = x1; + } else { + x1 = y1 = Infinity; + x2 = y2 = -Infinity; + while (++i < n) { + p = points[i]; + if (p.x < x1) { + x1 = p.x; + } + if (p.y < y1) { + y1 = p.y; + } + if (p.x > x2) { + x2 = p.x; + } + if (p.y > y2) { + y2 = p.y; + } + } + var dx = x2 - x1, + dy = y2 - y1; + if (dx > dy) { + y2 = y1 + dx; + } else { + x2 = x1 + dy; + } + } + } + function insert(n, p, x1, y1, x2, y2) { + if (isNaN(p.x) || isNaN(p.y)) { + return; + } + if (n.leaf) { + var v = n.point; + if (v) { + if (Math.abs(v.x - p.x) + Math.abs(v.y - p.y) < 0.01) { + insertChild(n, p, x1, y1, x2, y2); + } else { + n.point = null; + insertChild(n, v, x1, y1, x2, y2); + insertChild(n, p, x1, y1, x2, y2); + } + } else { + n.point = p; + } + } else { + insertChild(n, p, x1, y1, x2, y2); + } + } + function insertChild(n, p, x1, y1, x2, y2) { + var sx = (x1 + x2) * 0.5, + sy = (y1 + y2) * 0.5, + right = p.x >= sx, + bottom = p.y >= sy, + i = (bottom << 1) + right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = { leaf: true, nodes: [], point: null }); + if (right) { + x1 = sx; + } else { + x2 = sx; + } + if (bottom) { + y1 = sy; + } else { + y2 = sy; + } + insert(n, p, x1, y1, x2, y2); + } + function quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * 0.5, + sy = (y1 + y2) * 0.5, + children = node.nodes; + if (children[0]) { + quadtreeVisit(f, children[0], x1, y1, sx, sy); + } + if (children[1]) { + quadtreeVisit(f, children[1], sx, y1, x2, sy); + } + if (children[2]) { + quadtreeVisit(f, children[2], x1, sy, sx, y2); + } + if (children[3]) { + quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + } + var root = { leaf: true, nodes: [], point: null }; + root.add = function (p) { + insert(root, p, x1, y1, x2, y2); + }; + root.visit = function (f) { + quadtreeVisit(f, root, x1, y1, x2, y2); + }; + points.forEach(root.add); + return root; + }; + var ForceDirected = function (_nodes, _links) { + this.nodes = _nodes; + this.links = _links; + this.attraction_multiplier = 5; + this.repulsion_multiplier = 0.5; + this.width = 1200; + this.height = 800; + this.inited = false; + var EPSILON = 1 / 100000; + var attraction_constant; + var repulsion_constant; + var forceConstant; + var layout_iterations = 0; + var max_iterations = 100000; + var temperature = 0; + var scalar = 10; + var that = this; + var nodes_length = _nodes.length; + var links_length = _links.length; + this.getConfig = function () { + return [ + { label: "吸引力", attraction: 2 }, + { label: "斥力", force: 100 }, + ]; + }; + this.resetConfig = function (layoutConfig) { + if (layoutConfig) { + attraction_multiplier = Number(layoutConfig["attraction"]) || 2; + forceConstant = Number(layoutConfig["force"]) || 100; + var size = layoutConfig["size"] || [1200, 800]; + this.width = size[0]; + this.height = size[1]; + that.initAlgo(); + } + }; + this.initAlgo = function () { + temperature = 10.0; + layout_iterations = 0; + attraction_constant = that.attraction_multiplier * forceConstant; + repulsion_constant = that.repulsion_multiplier * forceConstant; + that.inited = true; + }; + this.runLayout = function () { + if (that.inited) { + that.goAlgo(); + } + }; + this.goAlgo = function () { + if (temperature > 1 / 100000) { + var i, j, delta, delta_length, force, change; + for (i = 0; i < nodes_length; i++) { + var node_v = that.nodes[i]; + node_v.layout = node_v.layout || {}; + if (i === 0) { + node_v.layout.offset = new Vector2(); + } + node_v.layout.force = 0; + node_v.layout.tmp_pos = + node_v.layout.tmp_pos || new Vector2().setVector(node_v); + for (j = i + 1; j < nodes_length; j++) { + var node_u = that.nodes[j]; + if (i != j) { + node_u.layout = node_u.layout || {}; + node_u.layout.tmp_pos = + node_u.layout.tmp_pos || new Vector2().setVector(node_u); + delta = node_v.layout.tmp_pos.clone().sub(node_u.layout.tmp_pos); + delta_length = Math.max( + EPSILON, + Math.sqrt(delta.clone().multiply(delta).sum()), + ); + force = (repulsion_constant * repulsion_constant) / delta_length; + node_v.layout.force += force; + node_u.layout.force += force; + if (i === 0) { + node_u.layout.offset = new Vector2(); + } + change = delta + .clone() + .multiply(new Vector2().setScalar(force / delta_length)); + node_v.layout.offset.add(change); + node_u.layout.offset.sub(change); + } + } + } + for (i = 0; i < links_length; i++) { + var link = that.links[i]; + delta = link.source.layout.tmp_pos + .clone() + .sub(link.target.layout.tmp_pos); + delta_length = Math.max( + EPSILON, + Math.sqrt(delta.clone().multiply(delta).sum()), + ); + force = (delta_length * delta_length) / attraction_constant; + link.source.layout.force -= force; + link.target.layout.force += force; + change = delta + .clone() + .multiply(new Vector2().setScalar(force / delta_length)); + link.target.layout.offset.add(change); + link.source.layout.offset.sub(change); + } + for (i = 0; i < nodes_length; i++) { + var node = that.nodes[i]; + delta_length = Math.max( + EPSILON, + Math.sqrt( + node.layout.offset.clone().multiply(node.layout.offset).sum(), + ), + ); + node.layout.tmp_pos.add( + node.layout.offset + .clone() + .multiply( + new Vector2().setScalar( + Math.min(delta_length, temperature) / delta_length, + ), + ), + ); + var tmpPosition = new Vector2(node.x, node.y, 0); + tmpPosition + .sub(node.layout.tmp_pos) + .divide(new Vector2().setScalar(scalar)); + node.x -= tmpPosition.x; + node.y -= tmpPosition.y; + } + temperature *= 1 - layout_iterations / max_iterations; + layout_iterations++; + } + }; + }; + function Vector2(x, y) { + this.x = x || 0; + this.y = y || 0; + } + Object.assign(Vector2.prototype, { + set: function (x, y) { + this.x = x; + this.y = y; + return this; + }, + setScalar: function (scalar) { + this.x = scalar; + this.y = scalar; + return this; + }, + setVector: function (v) { + this.x = v.x || 0; + this.y = v.y || 0; + return this; + }, + clone: function () { + return new this.constructor(this.x, this.y, this.z); + }, + add: function (v) { + this.x += v.x; + this.y += v.y; + return this; + }, + addScalar: function (s) { + this.x += s; + this.y += s; + return this; + }, + sub: function (v) { + this.x -= v.x; + this.y -= v.y; + return this; + }, + multiply: function (v) { + this.x *= v.x; + this.y *= v.y; + return this; + }, + divide: function (v) { + this.x /= v.x; + this.y /= v.y; + return this; + }, + sum: function () { + return this.x + this.y; + }, + equals: function (v) { + return v.x === this.x && v.y === this.y; + }, + }); + var HierarchicalLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.layerDistance = 100; + this.nodeDistance = 60; + this.treeSpacing = 0; + this.direction = "UD"; + this.sortMethod = "hubsize"; + this.treeIndex = -1; + this.continueOrBreak = 0; + this.boolTransition = true; + this.inited = false; + }; + HierarchicalLayout.prototype.getConfig = function () { + return [ + { label: "层间距", layerDistance: 100 }, + { label: "点间距", nodeDistance: 100 }, + { + label: "排列方式", + sortMethod: [ + { label: "连线方向", value: "directed" }, + { label: "度大小", value: "hubsize" }, + { label: "指定点", value: "selected" }, + ], + }, + { + label: "排列方向", + direction: [ + { label: "上下", value: "UD" }, + { label: "下上", value: "DU" }, + { label: "左右", value: "LR" }, + { label: "右左", value: "RL" }, + ], + }, + ]; + }; + HierarchicalLayout.prototype.resetConfig = function (layoutConfig) { + var self = this; + if (layoutConfig) { + self.layerDistance = Number(layoutConfig["layerDistance"]) || 100; + self.nodeDistance = Number(layoutConfig["nodeDistance"]) || 80; + self.sortMethod = layoutConfig["sortMethod"] || "hubsize"; + self.direction = layoutConfig["direction"] || "UD"; + } + self.initAlgo(); + }; + HierarchicalLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + HierarchicalLayout.prototype.initAlgo = function () { + var self = this; + self.treeIndex = -1; + self.continueOrBreak = 0; + self.nodeIdList = []; + self.selectedNodeIds = null; + self.positionedNodes = {}; + self.hierarchicalLevels = {}; + self.hierarchicalTrees = {}; + self.lastNodeOnLevel = {}; + self.hierarchicalChildrenReference = {}; + self.hierarchicalParentReference = {}; + self.distributionIndex = {}; + self.distributionOrdering = {}; + self.distributionOrderingPresence = {}; + self.nodesIdMap = {}; + self.edges = {}; + self.setData(); + self.setupHierarchicalLayout(); + self.nodes.forEach(function (node) { + var nodeId = String(node.id); + var nodeLocal = self.nodesIdMap[nodeId]; + var x = 0, + y = 0; + if (self.direction == "DU") { + x = nodeLocal.x; + y = -nodeLocal.y; + } else if (self.direction == "RL") { + x = -nodeLocal.x; + y = nodeLocal.y; + } else { + x = nodeLocal.x; + y = nodeLocal.y; + } + var posData = self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / 50) * (x - node.x); + posData.ydistance = (1.0 / 50) * (y - node.y); + node.layoutData = posData; + }); + self.inited = true; + }; + HierarchicalLayout.prototype.setData = function (selectedNodeId) { + var self = this; + self.nodes.forEach(function (node) { + var nodeId = String(node.id); + var nodeTemp = self.NodeLocal(nodeId); + self.nodesIdMap[nodeId] = nodeTemp; + self.nodeIdList.push(nodeId); + }); + self.links.forEach(function (edge, i) { + var id = String(i + 1); + var edgeLocal = self.Edge(edge.source.id, edge.target.id, id); + self.edges[id] = edgeLocal; + var fromId = edgeLocal.fromId; + var toId = edgeLocal.toId; + var fromNode = self.nodesIdMap[fromId]; + if (fromNode.edgesIds.indexOf(id) == -1) { + fromNode.edgesIds.push(id); + } + var toNode = self.nodesIdMap[toId]; + if (toNode.edgesIds.indexOf(id) == -1) { + toNode.edgesIds.push(id); + } + }); + if (self.sortMethod == "selected") { + var selectedNodes = self.nodes.filter(function (n) { + return n.selected == true; + }); + var _selectedNodeIds = selectedNodes.map((n) => { + return String(n.id); + }); + self.selectedNodeIds = _selectedNodeIds; + } + }; + HierarchicalLayout.prototype.setupHierarchicalLayout = function () { + var self = this; + if (self.sortMethod == "hubsize") { + self.determineLevelsByHubsize(); + } else if (self.sortMethod == "directed") { + self.determineLevelsByDirected(); + } else if (self.sortMethod == "selected") { + self.determineLevelsBySelected(); + } + self.nodeIdList.forEach(function (nodeId) { + if (self.hierarchicalLevels[nodeId] == null) { + self.hierarchicalLevels[nodeId] = 0; + } + }); + var distribution = self.getDistribution(); + self.crawlNetwork("generateMap", -1); + self.placeNodesByHierarchy(distribution); + self.condenseHierarchy(); + }; + HierarchicalLayout.prototype.determineLevelsByDirected = function () { + var self = this; + self.crawlNetwork("determineLevelsByDirected", -1); + var minLevel = 1000000000; + self.nodeIdList.forEach(function (nodeId) { + if (self.hierarchicalLevels[nodeId] != null) { + minLevel = Math.min(self.hierarchicalLevels[nodeId], minLevel); + } + }); + self.nodeIdList.forEach(function (nodeId) { + if (self.hierarchicalLevels[nodeId] != null) { + self.hierarchicalLevels[nodeId] = + self.hierarchicalLevels[nodeId] - minLevel; + } + }); + }; + HierarchicalLayout.prototype.determineLevelsBySelected = function () { + var self = this; + if (self.selectedNodeIds == null) { + self.determineLevelsByHubsize(); + } else { + self.selectedNodeIds.forEach(function (nodeId) { + self.hierarchicalLevels[nodeId] = 0; + }); + self.selectedNodeIds.forEach(function (nodeId) { + if (self.nodesIdMap[nodeId] != null) { + self.crawlNetwork("determineLevelsByHubsize", nodeId); + } + }); + self.determineLevelsByHubsize(); + } + }; + HierarchicalLayout.prototype.NodeLocal = function (nodeId) { + return { id: nodeId, edgesIds: [], x: 0.5, y: 0.5 }; + }; + HierarchicalLayout.prototype.Edge = function (fromId, toId, id) { + return { id: id, fromId: fromId, toId: toId }; + }; + HierarchicalLayout.prototype.determineLevelsByHubsize = function () { + var self = this; + var hubSize = 1; + while (hubSize > 0) { + var _hubSize = 0; + self.nodeIdList.forEach(function (nodeId) { + var node = self.nodesIdMap[nodeId]; + if (self.hierarchicalLevels[node.id] == null) { + _hubSize = + node.edgesIds.length < _hubSize ? _hubSize : node.edgesIds.length; + } + }); + hubSize = _hubSize; + if (hubSize == 0) { + return; + } + self.nodeIdList.forEach(function (nodeId) { + var node = self.nodesIdMap[nodeId]; + if (node.edgesIds.length == hubSize) { + self.crawlNetwork("determineLevelsByHubsize", node.id); + } + }); + } + }; + HierarchicalLayout.prototype.condenseHierarchy = function () { + var self = this; + var minPre = 1000000000, + maxPre = -1000000000; + var minAfter = 1000000000, + maxAfter = -1000000000; + for (var i = 0; i < self.treeIndex; i++) { + for (var nodeId in self.hierarchicalTrees) { + if (i == 0 && self.hierarchicalTrees[nodeId] == i) { + var pos = self.getPositionForHierarchy(self.nodesIdMap[nodeId]); + minPre = Math.min(pos, minPre); + maxPre = Math.max(pos, maxPre); + } + if (self.hierarchicalTrees[nodeId] == i + 1) { + var pos = self.getPositionForHierarchy(self.nodesIdMap[nodeId]); + minAfter = Math.min(pos, minAfter); + maxAfter = Math.max(pos, maxAfter); + } + } + var diff = 0; + for (var nodeId in self.hierarchicalTrees) { + if (self.hierarchicalTrees[nodeId] == i + 1) { + var node = self.nodesIdMap[nodeId]; + var pos = self.getPositionForHierarchy(node); + self.setPositionForHierarchy(node, pos + diff + self.treeSpacing, -1); + } + } + minPre = minAfter + diff + self.treeSpacing; + maxPre = maxAfter + diff + self.treeSpacing; + } + }; + HierarchicalLayout.prototype.shiftToCenter = function () { + var self = this; + var minY = 1000000000, + maxY = -1000000000; + var minX = 1000000000, + maxX = -1000000000; + self.nodeIdList.forEach(function (nodeId) { + var node = self.nodesIdMap[nodeId]; + minX = Math.min(minX, node.x); + maxX = Math.max(maxX, node.x); + minY = Math.min(minY, node.y); + maxY = Math.max(maxY, node.y); + }); + var width = maxX - minX; + var height = maxY - minY; + var ratioW = 1, + ratioH = 1; + var standardW = 5000, + standardH = 3000; + if (width > standardW) { + ratioW = standardW / width; + } + if (height > standardH) { + ratioH = standardH / height; + } + self.nodeIdList.forEach(function (nodeId) { + var node = self.nodesIdMap[nodeId]; + var nodeX = node.x; + node.x = Math.round((nodeX - minX) * ratioW); + var nodeY = node.y; + node.y = Math.round((nodeY - minY) * ratioH); + }); + }; + HierarchicalLayout.prototype.getDistribution = function () { + var self = this; + var distribution = {}; + self.nodeIdList.forEach(function (nodeId) { + var node = self.nodesIdMap[nodeId]; + var level = + self.hierarchicalLevels[nodeId] == null + ? 1 + : self.hierarchicalLevels[nodeId]; + if (self.direction == "UD" || self.direction == "DU") { + node.y = self.layerDistance * level; + } else { + node.x = self.layerDistance * level; + } + var temp = distribution[level]; + if (temp == null) { + temp = []; + } + temp.push(node); + distribution[level] = temp; + }); + return distribution; + }; + HierarchicalLayout.prototype.crawlNetwork = function (callbackFlag, nodeId) { + var self = this; + var startingNodeId = nodeId; + var progress = {}; + if (startingNodeId == -1) { + var _treeIndex = 0; + self.nodeIdList.forEach(function (id) { + var node = self.nodesIdMap[id]; + if (progress[id] == null) { + progress = self.crawler(progress, node, callbackFlag, _treeIndex); + } + _treeIndex++; + }); + } else { + var node = self.nodesIdMap[startingNodeId]; + progress = self.crawler(progress, node, callbackFlag, -111); + } + }; + HierarchicalLayout.prototype.crawler = function ( + progress, + node, + callbackFlag, + _treeIndex, + ) { + var self = this; + if (progress[node.id] == null) { + if (_treeIndex != -111) { + if (self.hierarchicalTrees[node.id] == null) { + self.hierarchicalTrees[node.id] = _treeIndex; + self.treeIndex = Math.max(_treeIndex, self.treeIndex); + } + } + progress[node.id] = true; + var edgesIdsLength = node.edgesIds.length; + for (var i = 0; i < edgesIdsLength; i++) { + var edgeId = node.edgesIds[i]; + var edge = self.edges[edgeId]; + var childNode = null; + if (edge.toId == node.id) { + childNode = self.nodesIdMap[edge.fromId]; + } else { + childNode = self.nodesIdMap[edge.toId]; + } + if (childNode != null && node.id != childNode.id) { + if (callbackFlag == "determineLevelsByHubsize") { + self.levelDownstream(node, childNode); + } else if (callbackFlag == "determineLevelsByDirected") { + self.levelByDirection(node, childNode, edge); + } else if (callbackFlag == "generateMap") { + self.fillInRelations(node, childNode); + } + self.crawler(progress, childNode, callbackFlag, _treeIndex); + } + } + } + return progress; + }; + HierarchicalLayout.prototype.levelDownstream = function (source, nodeB) { + var self = this; + if (self.hierarchicalLevels[nodeB.id] == null) { + if (self.hierarchicalLevels[source.id] == null) { + self.hierarchicalLevels[source.id] = 0; + } + self.hierarchicalLevels[nodeB.id] = + self.hierarchicalLevels[source.id] + 1; + } + }; + HierarchicalLayout.prototype.levelByDirection = function ( + source, + nodeB, + edge, + ) { + var self = this; + if (self.hierarchicalLevels[source.id] == null) { + self.hierarchicalLevels[source.id] = 1; + } + if (self.hierarchicalLevels[nodeB.id] == null) { + self.hierarchicalLevels[nodeB.id] = + self.hierarchicalLevels[source.id] + 1; + } + }; + HierarchicalLayout.prototype.fillInRelations = function ( + parentNode, + childNode, + ) { + var self = this; + if ( + self.hierarchicalLevels[childNode.id] > + self.hierarchicalLevels[parentNode.id] + ) { + if (self.hierarchicalChildrenReference[parentNode.id] == null) { + self.hierarchicalChildrenReference[parentNode.id] = []; + } + self.hierarchicalChildrenReference[parentNode.id].push(childNode.id); + if (self.hierarchicalParentReference[childNode.id] == null) { + self.hierarchicalParentReference[childNode.id] = []; + } + self.hierarchicalParentReference[childNode.id].push(parentNode.id); + } + }; + HierarchicalLayout.prototype.placeNodesByHierarchy = function (distribution) { + var self = this; + for (var nodeId in distribution) { + var nodesList = distribution[nodeId]; + var handledNodeCount = 0; + var nodesListLength = nodesList.length; + for (var i = 0; i < nodesListLength; i++) { + var node = nodesList[i]; + if (self.positionedNodes[node.id] == null) { + var pos = self.nodeDistance * handledNodeCount; + if (handledNodeCount > 0) { + pos = + self.getPositionForHierarchy(nodesList[i - 1]) + + self.nodeDistance; + } + self.setPositionForHierarchy(node, pos, nodeId); + self.validataPositionAndContinue(node, nodeId, pos); + handledNodeCount++; + } + } + } + }; + HierarchicalLayout.prototype.getPositionForHierarchy = function (node) { + if (this.direction == "UD" || this.direction == "DU") { + return node.x; + } + return node.y; + }; + HierarchicalLayout.prototype.setPositionForHierarchy = function ( + node, + position, + level, + ) { + var self = this; + if (level != -1) { + if (self.distributionOrdering[level] == null) { + self.distributionOrdering[level] = []; + self.distributionOrderingPresence[level] = {}; + } + if (self.distributionOrderingPresence[level][node.id] == null) { + self.distributionOrdering[level].push(node); + self.distributionIndex[node.id] = + self.distributionOrdering[level].length - 1; + } + self.distributionOrderingPresence[level][node.id] = true; + } + if (self.direction == "UD" || self.direction == "DU") { + node.x = position; + } else { + node.y = position; + } + }; + HierarchicalLayout.prototype.shiftBlock = function (parentId, diff) { + var self = this; + if (self.direction == "UD" || self.direction == "DU") { + self.nodesIdMap[parentId].x += diff; + } else { + self.nodesIdMap[parentId].y += diff; + } + }; + HierarchicalLayout.prototype.placeBranchNodes = function ( + parentNode, + parentLevel, + ) { + var self = this; + if (self.hierarchicalChildrenReference[parentNode.id] == null) { + return; + } + var childNodes = []; + var length = self.hierarchicalChildrenReference[parentNode.id].length; + for (var i = 0; i < length; i++) { + childNodes.push( + self.nodesIdMap[self.hierarchicalChildrenReference[parentNode.id][i]], + ); + } + var childNodesLength = childNodes.length; + for (var i = 0; i < childNodesLength; i++) { + var childNode = childNodes[i]; + var childNodeLevel = self.hierarchicalLevels[childNode.id]; + if ( + childNodeLevel > parentLevel && + self.positionedNodes[childNode.id] == null + ) { + var pos = 0; + if (i == 0) { + pos = self.getPositionForHierarchy(self.nodesIdMap[parentNode.id]); + } else { + pos = + self.getPositionForHierarchy(childNodes[i - 1]) + self.nodeDistance; + } + self.setPositionForHierarchy(childNode, pos, childNodeLevel); + self.validataPositionAndContinue(childNode, childNodeLevel, pos); + } + } + var minPos = 1000000000, + maxPos = -1000000000; + for (var i = 0; i < childNodesLength; i++) { + var childNode = childNodes[i]; + minPos = Math.min(minPos, self.getPositionForHierarchy(childNode)); + maxPos = Math.max(maxPos, self.getPositionForHierarchy(childNode)); + } + var _pos = (minPos + maxPos) / 2; + self.setPositionForHierarchy(parentNode, _pos, parentLevel); + }; + HierarchicalLayout.prototype.validataPositionAndContinue = function ( + node, + nodeId, + pos, + ) { + var self = this; + if (self.lastNodeOnLevel[nodeId] != undefined) { + var previousPos = self.getPositionForHierarchy( + self.nodesIdMap[self.lastNodeOnLevel[nodeId]], + ); + if (pos - previousPos < self.nodeDistance) { + var diff = previousPos + self.nodeDistance - pos; + self.shiftBlock(node.id, diff); + } + } + self.lastNodeOnLevel[nodeId] = node.id; + self.positionedNodes[node.id] = true; + self.placeBranchNodes(node, nodeId); + }; + HierarchicalLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + HierarchicalLayout.prototype.goAlgo = function () { + var self = this; + var position = null; + var nodes = self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var HiveLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.margin = 0; + this.radius = 50; + this.nlines = 5; + this.boolTransition = true; + this.intSteps = 50; + this.inited = false; + }; + HiveLayout.prototype.getConfig = function () { + return [{ label: "分支数", nlines: 5 }]; + }; + HiveLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.nlines = Number(layoutConfig["nlines"]) || 5; + } + this.initAlgo(); + }; + HiveLayout.prototype.newLayoutData = function () { + var layoutData = { + finishx: 0.0, + finishy: 0.0, + xdistance: 0.0, + ydistance: 0.0, + }; + return layoutData; + }; + HiveLayout.prototype.initAlgo = function () { + var self = this; + var nodeCount = Math.max(this.nodes.length * 6, 1200); + const nodes_segment = this.nodes.length / this.nlines; + const segment = nodeCount - (this.margin + this.radius); + const step = segment / nodes_segment; + const angle = (2 * Math.PI) / this.nlines; + let j = 0; + for (let i = 0; i < this.nodes.length; ++i) { + var node = this.nodes[i]; + var x = + nodeCount + + (this.radius + step * (i - j * nodes_segment)) * + Math.cos(angle * j + Math.PI / 2); + var y = + nodeCount + + (this.radius + step * (i - j * nodes_segment)) * + Math.sin(angle * j + Math.PI / 2); + j = Math.floor(i / nodes_segment); + var posData = self.newLayoutData(); + posData.finishx = x; + posData.finishy = y; + posData.xdistance = (1.0 / self.intSteps) * (x - node.x); + posData.ydistance = (1.0 / self.intSteps) * (y - node.y); + node.layoutData = posData; + } + this.inited = true; + }; + HiveLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + HiveLayout.prototype.goAlgo = function () { + var self = this; + var position = null; + var nodes = self.nodes; + var length = nodes.length; + for (var i = 0; i < length; i++) { + var n = nodes[i]; + position = n.layoutData; + if (position == null) { + continue; + } + if (self.boolTransition) { + var currentDistance = Math.abs(n.x - position.finishx); + var nextDistance = Math.abs( + n.x + position.xdistance - position.finishx, + ); + if (nextDistance < currentDistance) { + n.x += position.xdistance; + } else { + n.x = position.finishx; + } + currentDistance = Math.abs(n.y - position.finishy); + nextDistance = Math.abs(n.y + position.ydistance - position.finishy); + if (nextDistance < currentDistance) { + n.y += position.ydistance; + } else { + n.y = position.finishy; + } + if (n.x == position.finishx && n.y == position.finishy) { + n.layoutData = null; + } + } else { + n.x = position.finishx; + n.y = position.finishy; + n.layoutData = null; + } + } + }; + var AvoidOverlapLayout = function (nodes, links) { + this.nodes = nodes; + this.maxPadding = 10; + this.inited = false; + this.maxIterations = 0; + }; + AvoidOverlapLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + AvoidOverlapLayout.prototype.getConfig = function () { + var self = this; + return [{ label: "间距", maxPadding: self.maxPadding }]; + }; + AvoidOverlapLayout.prototype.resetConfig = function (layoutConfig) { + this.initAlgo(); + if (layoutConfig) { + this.maxPadding = Number(layoutConfig["maxPadding"]) || 2; + if (this.maxPadding < 0) { + this.maxPadding = 0; + } + } + }; + AvoidOverlapLayout.prototype.initAlgo = function () { + this.inited = true; + this.maxIterations = 0; + }; + AvoidOverlapLayout.prototype.goAlgo = function () { + var self = this; + if (this.inited) { + if (this.maxIterations++ < this.nodes.length / 4) { + var q = this.quadtree(this.nodes); + self.nodes.forEach(function (_node) { + q.visit(collide(_node)); + }); + } + } + function collide(node) { + var nr = node.radius * node.scaleX * 2 + self.maxPadding, + nx1 = node.x - nr, + nx2 = node.x + nr, + ny1 = node.y - nr, + ny2 = node.y + nr; + return function (quad, x1, y1, x2, y2) { + if (quad.point && quad.point !== node) { + var x = node.x - quad.point.x, + y = node.y - quad.point.y, + l = Math.sqrt(x * x + y * y), + r = nr + quad.point.radius; + if (l < r) { + l = ((l - r) / l) * 0.5; + node.x -= x *= l; + node.y -= y *= l; + quad.point.x += x; + quad.point.y += y; + } + } + return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; + }; + } + }; + AvoidOverlapLayout.prototype.quadtree = function (points) { + var p, + i = -1, + n = points.length; + if (n && isNaN(points[0].x)) { + points = points.map(function (p) { + return { x: p[0], y: p[1] }; + }); + } + var x1 = (y1 = Infinity); + var x2 = (y2 = -Infinity); + while (++i < n) { + p = points[i]; + if (p.x < x1) { + x1 = p.x; + } + if (p.y < y1) { + y1 = p.y; + } + if (p.x > x2) { + x2 = p.x; + } + if (p.y > y2) { + y2 = p.y; + } + } + var dx = x2 - x1, + dy = y2 - y1; + if (dx > dy) { + y2 = y1 + dx; + } else { + x2 = x1 + dy; + } + function insert(n, p, x1, y1, x2, y2) { + if (isNaN(p.x) || isNaN(p.y)) { + return; + } + if (n.leaf) { + var v = n.point; + if (v) { + if (Math.abs(v.x - p.x) + Math.abs(v.y - p.y) < 0.01) { + insertChild(n, p, x1, y1, x2, y2); + } else { + n.point = null; + insertChild(n, v, x1, y1, x2, y2); + insertChild(n, p, x1, y1, x2, y2); + } + } else { + n.point = p; + } + } else { + insertChild(n, p, x1, y1, x2, y2); + } + } + function insertChild(n, p, x1, y1, x2, y2) { + var sx = (x1 + x2) * 0.5, + sy = (y1 + y2) * 0.5, + right = p.x >= sx, + bottom = p.y >= sy, + i = (bottom << 1) + right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = { leaf: true, nodes: [], point: null }); + if (right) { + x1 = sx; + } else { + x2 = sx; + } + if (bottom) { + y1 = sy; + } else { + y2 = sy; + } + insert(n, p, x1, y1, x2, y2); + } + function quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * 0.5, + sy = (y1 + y2) * 0.5, + children = node.nodes; + if (children[0]) { + quadtreeVisit(f, children[0], x1, y1, sx, sy); + } + if (children[1]) { + quadtreeVisit(f, children[1], sx, y1, x2, sy); + } + if (children[2]) { + quadtreeVisit(f, children[2], x1, sy, sx, y2); + } + if (children[3]) { + quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + } + var root = { leaf: true, nodes: [], point: null }; + root.add = function (p) { + insert(root, p, x1, y1, x2, y2); + }; + root.visit = function (f) { + quadtreeVisit(f, root, x1, y1, x2, y2); + }; + points.forEach(root.add); + return root; + }; + var TypeGatherLayout = function (nodes, links) { + this.nodes = nodes; + this.links = links; + this.SPEED_DIVISOR = 800; + this.AREA_MULTIPLICATOR = 100000; + this.area = 2; + this.speed = 5; + this.inited = false; + }; + TypeGatherLayout.prototype.getConfig = function () { + var self = this; + return [ + { label: "间距", area: self.area }, + { label: "移动速度", speed: self.speed }, + ]; + }; + TypeGatherLayout.prototype.resetConfig = function (layoutConfig) { + this.initAlgo(); + if (layoutConfig) { + this.area = Number(layoutConfig["area"]) || 2; + this.speed = Number(layoutConfig["speed"]) || 5; + } + }; + TypeGatherLayout.prototype.initAlgo = function () { + var self = this; + this.area = 200; + this.speed = 5; + this.nodes.forEach(function (n) { + n.layoutData = self.newLayoutData(); + }); + this.maxDisplace = Math.sqrt(self.AREA_MULTIPLICATOR * self.area) / 10; + this.k = Math.sqrt( + (self.AREA_MULTIPLICATOR * self.area) / (1 + self.nodes.length), + ); + this.inited = true; + }; + TypeGatherLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + TypeGatherLayout.prototype.goAlgo = function () { + var self = this; + self.repulsiveForce(); + self.attractiveForce(); + self.sameTypeAttractive(); + self.resetNodePosition(); + }; + TypeGatherLayout.prototype.repulsiveForce = function () { + var self = this; + self.nodes.forEach(function (N1, i) { + self.nodes.forEach(function (N2, j) { + if (i != j) { + var xDist = N1.x - N2.x; + var yDist = N1.y - N2.y; + var dist = Math.sqrt(xDist * xDist + yDist * yDist); + if (dist > 1.0) { + var repulsiveF = (self.k * self.k) / dist; + var layoutData = N1.layoutData; + layoutData.dx += (xDist / dist) * repulsiveF * 0.01; + layoutData.dy += (yDist / dist) * repulsiveF * 0.01; + } + } + }); + }); + }; + TypeGatherLayout.prototype.attractiveForce = function () { + var self = this; + self.links.forEach(function (E) { + var Nf = E.source; + var Nt = E.target; + var xDist = Nf.x - Nt.x; + var yDist = Nf.y - Nt.y; + var dist = Math.sqrt(xDist * xDist + yDist * yDist); + var attractiveF = (dist * dist) / self.k; + if (dist > 1.0) { + var sourceLayoutData = Nf.layoutData; + var targetLayoutData = Nt.layoutData; + sourceLayoutData.dx -= (xDist / dist) * attractiveF; + sourceLayoutData.dy -= (yDist / dist) * attractiveF; + targetLayoutData.dx += (xDist / dist) * attractiveF; + targetLayoutData.dy += (yDist / dist) * attractiveF; + } + }); + }; + TypeGatherLayout.prototype.sameTypeAttractive = function () { + var self = this; + self.nodes.forEach(function (N1, i) { + self.nodes.forEach(function (N2, j) { + if (i != j && N1.cluster == N2.cluster) { + var xDist = N1.x - N2.x; + var yDist = N1.y - N2.y; + var dist = Math.sqrt(xDist * xDist + yDist * yDist); + if (dist > 0) { + var attractiveF = (dist * dist) / self.k; + var sourceLayoutData = N1.layoutData; + var targetLayoutData = N2.layoutData; + sourceLayoutData.dx -= (xDist / dist) * attractiveF; + sourceLayoutData.dy -= (yDist / dist) * attractiveF; + targetLayoutData.dx += (xDist / dist) * attractiveF; + targetLayoutData.dy += (yDist / dist) * attractiveF; + } + } + }); + }); + }; + TypeGatherLayout.prototype.resetNodePosition = function () { + var self = this; + self.nodes.forEach(function (node) { + node.layoutData.dx *= self.speed / self.SPEED_DIVISOR; + node.layoutData.dy *= self.speed / self.SPEED_DIVISOR; + var layoutData = node.layoutData; + var xDist = layoutData.dx; + var yDist = layoutData.dy; + var dist = Math.sqrt( + layoutData.dx * layoutData.dx + layoutData.dy * layoutData.dy, + ); + if (dist > 0 && !node.fixed) { + var limitedDist = Math.min( + self.maxDisplace * (self.speed / self.SPEED_DIVISOR), + dist, + ); + node.x = node.x + (xDist / dist) * limitedDist; + node.y = node.y + (yDist / dist) * limitedDist; + } + }); + }; + TypeGatherLayout.prototype.newLayoutData = function () { + var layoutData = { dx: 0.0, dy: 0.0 }; + return layoutData; + }; + var SpiralLayout = function SpiralLayout(nodes) { + this.nodes = nodes; + this.radius = NaN; + this.spacing = 10; + this.clockwise = true; + this.center = [100, 100]; + }; + SpiralLayout.prototype.getConfig = function () { + var self = this; + return [{ label: "间距", spacing: 10 }]; + }; + SpiralLayout.prototype.resetConfig = function (layoutConfig) { + if (layoutConfig) { + this.spacing = Number(layoutConfig["spacing"]) || 10; + this.center = layoutConfig["center"] || [100, 100]; + } + this.initAlgo(); + }; + SpiralLayout.prototype.initAlgo = function () { + var self = this; + var originx = self.center[0]; + var originy = self.center[1]; + var nodes = self.nodes.sort(function (n1, n2) { + var x = (n1.inLinks || []).length + (n1.outLinks || []).length; + var y = (n2.inLinks || []).length + (n2.outLinks || []).length; + if (x > y) { + return -1; + } else if (x < y) { + return 1; + } else { + return 0; + } + }); + var root = nodes[0]; + var space = self.spacing; + var cw = self.clockwise ? 1 : -1; + var rad = self.radius; + if (rad <= 0 || isNaN(rad) || !isFinite(rad)) { + rad = this.diameter(root); + } + var dia = this.diameter(root); + var angle = cw * Math.PI; + root.x = originx; + root.y = originy; + var nodeLength = nodes.length; + for (var i = 1; i < nodeLength; i++) { + var cos = Math.cos(angle); + var sin = Math.sin(angle); + var x = rad * (cos + angle * sin); + var y = rad * (sin - angle * cos); + var vert = nodes[i]; + vert.x = x + originx; + vert.y = y + originy; + var nextvert = nodes[i + 1]; + var dia = self.diameter(vert) / 2 + self.diameter(nextvert) / 2; + angle += cw * Math.atan((dia + space) / Math.sqrt(x * x + y * y)); + } + }; + SpiralLayout.prototype.diameter = function (v) { + if (!v) { + return 0; + } + return Math.sqrt(v.radius * v.radius + v.radius * v.radius); + }; + SpiralLayout.prototype.runLayout = function () { + if (this.inited) { + this.goAlgo(); + } + }; + SpiralLayout.prototype.goAlgo = function () {}; + var layoutFac = function (_graph, config) { + console.log(_graph); + if (!_graph || _graph == null) { + return; + } + this.config = config || {}; + this.graph = { nodes: _graph.nodes || [], links: _graph.links || [] }; + }; + layoutFac.prototype.createLayout = function (layoutType) { + if (layoutType == null) { + return null; + } + return this.getLayout(layoutType); + }; + layoutFac.prototype.getLayout = function (layoutType, configs) { + var _self = this; + var nodes = _self.graph.nodes || []; + var links = _self.graph.links || []; + console.log(nodes); + var layout; + switch (layoutType) { + case "concentric": + layout = new ConcentricLayout(nodes, links); + break; + case "singleCirlce": + layout = new CircleLayout(nodes, links); + break; + case "dualCirlce": + layout = new DualCircleLayout(nodes, links); + break; + case "layerCircle": + layout = new LayerLayout(nodes, links); + break; + case "fr": + layout = new FRlayout(nodes, links); + break; + case "fastFR": + layout = new ForceD3Layout(nodes, links); + break; + case "frDirect": + layout = new ForceDirected(nodes, links); + break; + case "fruchtermanReingold": + layout = new FruchtermanReingoldLayout(nodes, links); + break; + case "spring2": + layout = new SpringLayout2(nodes, links); + break; + case "kk": + layout = new KKLayout(nodes, links); + break; + case "arf": + layout = new ARFLayout(nodes, links); + break; + case "tree": + layout = new TreeLayout(nodes, links); + break; + case "radiatree": + layout = new RadiaTreeLayout(nodes, links); + break; + case "balloon": + layout = new BalloonLayout(nodes, links); + break; + case "noverlap": + layout = new AvoidOverlapLayout(nodes, links); + break; + case "sphere": + layout = new SphereLayout(nodes, links); + break; + case "layered": + layout = new LayeredLayout(nodes, links); + break; + case "topoCircle": + layout = new TopoTreeLayout(nodes, links); + break; + case "hubsize": + layout = new HierarchicalLayout(nodes, links); + break; + case "hive": + layout = new HiveLayout(nodes, links); + break; + case "scale": + layout = new ScaleLayout(nodes, null, 1); + break; + case "rotate": + layout = new RotateLayout(nodes, null, 0); + break; + case "grid": + layout = new GirdLayout(nodes); + break; + case "gather": + layout = new TypeGatherLayout(nodes, links); + break; + case "spiral": + layout = new SpiralLayout(nodes); + break; + default: + break; + } + if (!layout) { + return null; + } + return layout; + }; + var LayoutFactory = layoutFac; + if (typeof module !== "undefined" && typeof exports === "object") { + module.exports = LayoutFactory; + } else if (typeof define === "function" && (define.amd || define.cmd)) { + define(function () { + return LayoutFactory; + }); + } else { + this.LayoutFactory = LayoutFactory; + } +}).call(this || (typeof window !== "undefined" ? window : global)); diff --git a/kcui/src/assets/js/graphvis.min.20241008.js b/kcui/src/assets/js/graphvis.min.20241008.js new file mode 100644 index 0000000..35dc98a --- /dev/null +++ b/kcui/src/assets/js/graphvis.min.20241008.js @@ -0,0 +1,9648 @@ +/* eslint-disable */ + +(function () { + const DGraph = { + version: "4.5.1", + Group_zIndex: 1, + Link_zIndex: 2, + Node_zIndex: 3, + SceneMode: { normal: "normal", drag: "drag", select: "select" }, + Element: function () { + (this.initialize = function () { + this.elementType = "element"; + this.serializedProperties = ["elementType"]; + this.propertiesStack = []; + }), + (this.removeHandler = function () {}), + (this.save = function () { + var self = this; + var newObj = {}; + self.serializedProperties.forEach(function (properties) { + newObj[properties] = self[properties]; + }); + self.propertiesStack.push(newObj); + }), + (this.restore = function () { + var self = this; + if ( + null != self.propertiesStack && + 0 != self.propertiesStack.length + ) { + var stack = self.propertiesStack.pop(); + self.serializedProperties.forEach(function (attr) { + self[attr] = stack[attr]; + }); + } + }); + }, + }; + !(function (DGraph) { + function MessageBus(name) { + var self = this; + this.name = name; + this.messageMap = {}; + this.messageCount = 0; + (this.subscribe = function (name, caller) { + var message = self.messageMap[name]; + null == message && (self.messageMap[name] = []), + self.messageMap[name].push(caller), + self.messageCount++; + }), + (this.unsubscribe = function (name) { + var message = self.messageMap[name]; + null != message && + ((self.messageMap[name] = null), + delete self.messageMap[name], + self.messageCount--); + }), + (this.publish = function (name, caller, d) { + var message = self.messageMap[name]; + if (null != message) { + for (var i = 0; i < message.length; i++) { + d + ? !(function (event, args) { + setTimeout(function () { + event(args); + }, 10); + })(message[i], caller) + : message[i](caller); + } + } + }); + } + function getEventPosition(event) { + var pos = null; + if (!event.pageX) { + pos = cloneEvent(event); + pos.pageX = + event.clientX + document.body.scrollLeft - document.body.clientLeft; + pos.pageY = + event.clientY + document.body.scrollTop - document.body.clientTop; + } else { + pos = cloneEvent(event); + } + return pos; + } + function cloneEvent(event) { + var _event = {}; + for (var key in event) + "returnValue" != key && + "keyLocation" != key && + (_event[key] = event[key]); + return _event; + } + function clone(obj) { + var newObj = {}; + for (var key in obj) newObj[key] = obj[key]; + return newObj; + } + function removeFromArray(arr, item) { + for (var i = 0; i < arr.length; i++) { + var obj = arr[i]; + if (obj == item || obj["id"] == item["id"]) { + arr.splice(i, 1); + break; + } + } + return arr; + } + function getOffsetPosition(a) { + if (!a) return { left: 0, top: 0 }; + var b = 0, + c = 0; + if ("getBoundingClientRect" in document.documentElement) { + var d = a.getBoundingClientRect(), + e = a.ownerDocument, + f = e.body, + g = e.documentElement, + h = g.clientTop || f.clientTop || 0, + i = g.clientLeft || f.clientLeft || 0, + b = + d.top + (self.pageYOffset || (g && g.scrollTop) || f.scrollTop) - h, + c = + d.left + + (self.pageXOffset || (g && g.scrollLeft) || f.scrollLeft) - + i; + } else { + (b += a.offsetTop || 0), (c += a.offsetLeft || 0), (a = a.offsetParent); + } + return { left: c, top: b }; + } + function getDistance(pointA, pointB, x1, y1) { + var dx, dy; + return ( + null == x1 && null == y1 + ? ((dx = pointB.x - pointA.x), (dy = pointB.y - pointA.y)) + : ((dx = x1 - pointA), (dy = y1 - pointB)), + Math.sqrt(dx * dx + dy * dy) + ); + } + function isPointInLine(mousePoint, startPoint, endPoint) { + var distance = DGraph.util.getDistance(startPoint, endPoint), + dis1 = DGraph.util.getDistance(startPoint, mousePoint), + dis2 = DGraph.util.getDistance(endPoint, mousePoint), + flag = Math.abs(dis1 + dis2 - distance) <= 0.5; + return flag; + } + function lineVir(x1, y1, x2, y2) { + var angle = (y2 - y1) / (x2 - x1), + distance = y1 - x1 * angle; + function bound(num) { + return num * angle + distance; + } + return ( + (bound.k = angle), + (bound.b = distance), + (bound.x1 = x1), + (bound.x2 = x2), + (bound.y1 = y1), + (bound.y2 = y2), + bound + ); + } + function inRange(cpoint, point1, point2) { + var distance1 = Math.abs(point1 - point2), + distance2 = Math.abs(point1 - cpoint), + distance3 = Math.abs(point2 - cpoint), + distance = Math.abs(distance1 - (distance2 + distance3)); + return 0.000001 > distance ? true : false; + } + function isPointInLineSeg(pointA, pointB, viaPoint) { + return ( + inRange(pointA, viaPoint.x1, viaPoint.x2) && + inRange(pointB, viaPoint.y1, viaPoint.y2) + ); + } + function intersection(lineA, lineB) { + if (lineA.k == lineB.k) { + return null; + } + var pointX, pointY; + if (Infinity == lineA.k || lineA.k == -Infinity) { + (pointX = lineA.x1), (pointY = lineB(lineA.x1)); + } else if (Infinity == lineB.k || lineB.k == -Infinity) { + (pointX = lineB.x1), (pointY = lineA(lineB.x1)); + } else { + (pointX = (lineB.b - lineA.b) / (lineA.k - lineB.k)), + (pointY = lineA(pointX)); + } + if (0 == isPointInLineSeg(pointX, pointY, lineA)) { + return null; + } + if (0 == isPointInLineSeg(pointX, pointY, lineB)) { + return null; + } + return { x: pointX, y: pointY }; + } + function intersectionLineBound(point, bound) { + var line = lineVir(bound.left, bound.top, bound.left, bound.bottom), + insertPoint = intersection(point, line); + if (null == insertPoint) { + line = lineVir(bound.left, bound.top, bound.right, bound.top); + insertPoint = intersection(point, line); + if (null == insertPoint) { + (line = lineVir(bound.right, bound.top, bound.right, bound.bottom)), + (insertPoint = intersection(point, line)); + if (null == insertPoint) { + (line = lineVir( + bound.left, + bound.bottom, + bound.right, + bound.bottom, + )), + (insertPoint = intersection(point, line)); + } + } + } + return insertPoint; + } + function pointOnCircle(x, y, radius, angle) { + return { + x: x + radius * Math.cos(angle), + y: y + radius * Math.sin(angle), + }; + } + function findAngle(sx, sy, ex, ey) { + var tmp = Math.atan((ey - sy) / (ex - sx)); + if (ex - sx >= 0) { + return tmp; + } else { + return tmp + Math.PI; + } + } + function containStroke(x0, y0, x1, y1, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + let _l = lineWidth; + let _a = 0; + let _b = x0; + if ( + (y > y0 + _l && y > y1 + _l) || + (y < y0 - _l && y < y1 - _l) || + (x > x0 + _l && x > x1 + _l) || + (x < x0 - _l && x < x1 - _l) + ) { + return false; + } + if (x0 !== x1) { + _a = (y0 - y1) / (x0 - x1); + _b = (x0 * y1 - x1 * y0) / (x0 - x1); + } else { + return Math.abs(x - x0) <= _l / 2; + } + let tmp = _a * x - y + _b; + let _s = (tmp * tmp) / (_a * _a + 1); + return _s <= ((_l / 2) * _l) / 2; + } + function containBerzierStroke( + x0, + y0, + x1, + y1, + x2, + y2, + x3, + y3, + lineWidth, + x, + y, + ) { + if (lineWidth === 0) { + return false; + } + let _l = lineWidth; + if ( + (y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l) || + (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l) || + (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l) || + (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l) + ) { + return false; + } + let d = cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null); + return d <= _l / 2; + } + function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) { + var t; + var interval = 0.005; + var EPSILON_NUMERIC = 1e-4; + var d = Infinity; + var prev; + var next; + var d1; + var d2; + var _v0 = [], + _v1 = [], + _v2 = []; + _v0[0] = x; + _v0[1] = y; + for (var _t = 0; _t < 1; _t += 0.05) { + _v1[0] = cubicAt(x0, x1, x2, x3, _t); + _v1[1] = cubicAt(y0, y1, y2, y3, _t); + d1 = distanceSquare(_v0, _v1); + if (d1 < d) { + t = _t; + d = d1; + } + } + d = Infinity; + for (var i = 0; i < 32; i++) { + if (interval < EPSILON_NUMERIC) { + break; + } + prev = t - interval; + next = t + interval; + _v1[0] = cubicAt(x0, x1, x2, x3, prev); + _v1[1] = cubicAt(y0, y1, y2, y3, prev); + d1 = distanceSquare(_v1, _v0); + if (prev >= 0 && d1 < d) { + t = prev; + d = d1; + } else { + _v2[0] = cubicAt(x0, x1, x2, x3, next); + _v2[1] = cubicAt(y0, y1, y2, y3, next); + d2 = distanceSquare(_v2, _v0); + if (next <= 1 && d2 < d) { + t = next; + d = d2; + } else { + interval *= 0.5; + } + } + } + if (out) { + out[0] = cubicAt(x0, x1, x2, x3, t); + out[1] = cubicAt(y0, y1, y2, y3, t); + } + return Math.sqrt(d); + } + function containQuadraticStroke(x0, y0, x1, y1, x2, y2, lineWidth, x, y) { + if (lineWidth === 0) { + return false; + } + let _l = lineWidth; + if ( + (y > y0 + _l && y > y1 + _l && y > y2 + _l) || + (y < y0 - _l && y < y1 - _l && y < y2 - _l) || + (x > x0 + _l && x > x1 + _l && x > x2 + _l) || + (x < x0 - _l && x < x1 - _l && x < x2 - _l) + ) { + return false; + } + let d = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null); + return d <= _l / 2; + } + function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) { + var t; + var interval = 0.005; + var EPSILON_NUMERIC = 1e-4; + var d = Infinity; + var d1 = 0; + var _v0 = [], + _v1 = [], + _v2 = []; + _v0[0] = x; + _v0[1] = y; + for (var _t = 0; _t < 1; _t += 0.05) { + _v1[0] = quadraticAt(x0, x1, x2, _t); + _v1[1] = quadraticAt(y0, y1, y2, _t); + d1 = distanceSquare(_v0, _v1); + if (d1 < d) { + t = _t; + d = d1; + } + } + d = Infinity; + for (var i = 0; i < 32; i++) { + if (interval < EPSILON_NUMERIC) { + break; + } + var prev = t - interval; + var next = t + interval; + _v1[0] = quadraticAt(x0, x1, x2, prev); + _v1[1] = quadraticAt(y0, y1, y2, prev); + d1 = distanceSquare(_v1, _v0); + if (prev >= 0 && d1 < d) { + t = prev; + d = d1; + } else { + _v2[0] = quadraticAt(x0, x1, x2, next); + _v2[1] = quadraticAt(y0, y1, y2, next); + var d2 = distanceSquare(_v2, _v0); + if (next <= 1 && d2 < d) { + t = next; + d = d2; + } else { + interval *= 0.5; + } + } + } + if (out) { + out[0] = quadraticAt(x0, x1, x2, t); + out[1] = quadraticAt(y0, y1, y2, t); + } + return Math.sqrt(d); + } + function distanceSquare(v1, v2) { + return ( + (v1[0] - v2[0]) * (v1[0] - v2[0]) + (v1[1] - v2[1]) * (v1[1] - v2[1]) + ); + } + function cubicAt(p0, p1, p2, p3, t) { + var onet = 1 - t; + return ( + onet * onet * (onet * p0 + 3 * t * p1) + + t * t * (t * p3 + 3 * onet * p2) + ); + } + function quadraticAt(p0, p1, p2, t) { + var onet = 1 - t; + return onet * (onet * p0 + 2 * t * p1) + t * t * p2; + } + function near(x, y, x1, y1, radius) { + let distance = Math.sqrt(Math.pow(x1 - x, 2) + Math.pow(y1 - y, 2)); + return distance <= radius; + } + function getSelfLoopControlPoints(x, y, size) { + return { x1: x + size * 7, y1: y, x2: x, y2: y - size * 7 }; + } + function getPointOnBezierCurve(t, x1, y1, x2, y2, cx, cy, dx, dy) { + var B0_t = Math.pow(1 - t, 3), + B1_t = 3 * t * Math.pow(1 - t, 2), + B2_t = 3 * Math.pow(t, 2) * (1 - t), + B3_t = Math.pow(t, 3); + return { + x: B0_t * x1 + B1_t * cx + B2_t * dx + B3_t * x2, + y: B0_t * y1 + B1_t * cy + B2_t * dy + B3_t * y2, + }; + } + function getQuadraticControlPoint(x1, y1, x2, y2, curverNum, curveness) { + curveness = curveness || 0.5; + return { + x: (x1 + x2) * curveness + (y2 - y1) / (curverNum || 4), + y: (y1 + y2) * curveness + (x1 - x2) / (curverNum || 4), + }; + } + function getPointOnQuadraticCurve(t, x1, y1, x2, y2, xi, yi) { + return { + x: Math.pow(1 - t, 2) * x1 + 2 * (1 - t) * t * xi + Math.pow(t, 2) * x2, + y: Math.pow(1 - t, 2) * y1 + 2 * (1 - t) * t * yi + Math.pow(t, 2) * y2, + }; + } + function isInPolygon(checkPoint, polygonPoints) { + var counter = 0; + var i; + var xinters; + var p1, p2; + var pointCount = polygonPoints.length; + p1 = polygonPoints[0]; + for (i = 1; i <= pointCount; i++) { + p2 = polygonPoints[i % pointCount]; + if ( + checkPoint[0] > Math.min(p1[0], p2[0]) && + checkPoint[0] <= Math.max(p1[0], p2[0]) + ) { + if (checkPoint[1] <= Math.max(p1[1], p2[1])) { + if (p1[0] != p2[0]) { + xinters = + ((checkPoint[0] - p1[0]) * (p2[1] - p1[1])) / (p2[0] - p1[0]) + + p1[1]; + if (p1[1] == p2[1] || checkPoint[1] <= xinters) { + counter++; + } + } + } + } + p1 = p2; + } + if (counter % 2 == 0) { + return false; + } else { + return true; + } + } + function findDotsAtSegment(p1x, p1y, c1x, c1y, c2x, c2y, p2x, p2y, t) { + var t1 = 1 - t, + t13 = Math.pow(t1, 3), + t12 = Math.pow(t1, 2), + t2 = t * t, + t3 = t2 * t, + x = t13 * p1x + t12 * 3 * t * c1x + t1 * 3 * t * t * c2x + t3 * p2x, + y = t13 * p1y + t12 * 3 * t * c1y + t1 * 3 * t * t * c2y + t3 * p2y, + mx = p1x + 2 * t * (c1x - p1x) + t2 * (c2x - 2 * c1x + p1x), + my = p1y + 2 * t * (c1y - p1y) + t2 * (c2y - 2 * c1y + p1y), + nx = c1x + 2 * t * (c2x - c1x) + t2 * (p2x - 2 * c2x + c1x), + ny = c1y + 2 * t * (c2y - c1y) + t2 * (p2y - 2 * c2y + c1y), + ax = t1 * p1x + t * c1x, + ay = t1 * p1y + t * c1y, + cx = t1 * c2x + t * p2x, + cy = t1 * c2y + t * p2y, + alpha = 90 - (Math.atan2(mx - nx, my - ny) * 180) / Math.PI; + (mx > nx || my < ny) && (alpha += 180); + return { + x: x, + y: y, + m: { x: mx, y: my }, + n: { x: nx, y: ny }, + start: { x: ax, y: ay }, + end: { x: cx, y: cy }, + alpha: alpha, + }; + } + function calPointOnCircle(x, y, radius, percentage) { + var angle = percentage * 2 * Math.PI; + return { + x: x + radius * Math.cos(angle), + y: y - radius * Math.sin(angle), + }; + } + function calculateAngle(sx, sy, tx, ty) { + var x = Math.abs(sx - tx); + var y = Math.abs(sy - ty); + var z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); + var radina = Math.acos(y / z); + var angle = Math.floor(180 / (Math.PI / radina)); + if (tx > sx && ty > sy) { + angle = 180 - angle; + } + if (tx == sx && ty > sy) { + angle = 180; + } + if (tx > sx && ty == sy) { + angle = 90; + } + if (tx < sx && ty > sy) { + angle = 180 + angle; + } + if (tx < sx && ty == sy) { + angle = 270; + } + if (tx < sx && ty < sy) { + angle = 360 - angle; + } + return angle; + } + function createWebWorker(content) { + const blob = new Blob([content], { type: "application/javascript" }); + const blobURL = URL.createObjectURL(blob); + const worker = new Worker(blobURL); + return worker; + } + (requestAnimationFrame = + window.requestAnimationFrame || + window.mozRequestAnimationFrame || + window.webkitRequestAnimationFrame || + window.msRequestAnimationFrame || + window.oRequestAnimationFrame || + function (callback) { + setTimeout(callback, 1000 / 60); + }), + (cancelAnimationFrame = + window.cancelAnimationFrame || + window.webkitCancelAnimationFrame || + window.mozCancelAnimationFrame || + window.cancelRequestAnimationFrame || + window.webkitCancelRequestAnimationFrame || + window.mozCancelRequestAnimationFrame || + function (id) { + clearTimeout(id); + }); + DGraph.util = { + getDistance: getDistance, + MessageBus: MessageBus, + isFirefox: navigator.userAgent.indexOf("Firefox") > 0, + isIE: !( + !window.attachEvent || -1 !== navigator.userAgent.indexOf("Opera") + ), + isChrome: null != navigator.userAgent.toLowerCase().match(/chrome/), + clone: clone, + isPointInLine: isPointInLine, + removeFromArray: removeFromArray, + getEventPosition: getEventPosition, + cloneEvent: cloneEvent, + getOffsetPosition: getOffsetPosition, + lineVir: lineVir, + intersection: intersection, + intersectionLineBound: intersectionLineBound, + pointOnCircle: pointOnCircle, + calPointOnCircle: calPointOnCircle, + findAngle: findAngle, + containStroke: containStroke, + containBerzierStroke: containBerzierStroke, + containQuadraticStroke: containQuadraticStroke, + near: near, + isInPolygon: isInPolygon, + findDotsAtSegment: findDotsAtSegment, + getSelfLoopControlPoints: getSelfLoopControlPoints, + getPointOnBezierCurve: getPointOnBezierCurve, + getQuadraticControlPoint: getQuadraticControlPoint, + getPointOnQuadraticCurve: getPointOnQuadraticCurve, + calculateAngle: calculateAngle, + createWebWorker: createWebWorker, + }; + DGraph.imgStore = {}; + CanvasRenderingContext2D.prototype.DGraphRoundRect = function ( + x, + y, + width, + height, + borderRadius, + ) { + if ("undefined" == typeof borderRadius) { + if (width < 2 * borderRadius) { + borderRadius = width / 2; + } + if (height < 2 * borderRadius) { + borderRadius = hieght / 2; + } + this.beginPath(); + this.rect(x, y, width, height); + this.closePath(); + } else { + if (width < 2 * borderRadius) { + borderRadius = width / 2; + } + if (height < 2 * borderRadius) { + borderRadius = height / 2; + } + this.beginPath(); + this.moveTo(x + borderRadius, y); + this.arcTo(x + width, y, x + width, y + height, borderRadius); + this.arcTo(x + width, y + height, x, y + height, borderRadius); + this.arcTo(x, y + height, x, y, borderRadius); + this.arcTo(x, y, x + width, y, borderRadius); + this.closePath(); + } + }; + CanvasRenderingContext2D.prototype.drawArrowLine = function ( + x0, + y0, + x1, + y1, + width, + ) { + if (width === void 0) { + width = 3; + } + if (width < 3) width = 3; + var polarCoordinate2canvasCoordinate = function (x0, y0, r, radian) { + var x = r * Math.cos(radian); + var y = r * Math.sin(radian); + x += x0; + y += y0; + return { x: x, y: y }; + }; + var distance = Math.sqrt((y1 - y0) * (y1 - y0) + (x1 - x0) * (x1 - x0)); + var radian = Math.asin(Math.abs(y1 - y0) / distance); + if (x0 > x1 && y1 > y0) { + radian = Math.PI - radian; + } else if (x0 > x1 && y0 > y1) { + radian += Math.PI; + } else if (x1 > x0 && y0 > y1) { + radian = 2 * Math.PI - radian; + } + var _a = polarCoordinate2canvasCoordinate( + x0, + y0, + distance - width * 2, + radian, + ), + x = _a.x, + y = _a.y; + var p1 = polarCoordinate2canvasCoordinate( + x, + y, + width, + radian - Math.PI * 0.5, + ); + var p2 = polarCoordinate2canvasCoordinate( + x, + y, + width * 2, + radian - Math.PI * 0.5, + ); + var p3 = polarCoordinate2canvasCoordinate( + x, + y, + width, + radian + Math.PI * 0.5, + ); + var p4 = polarCoordinate2canvasCoordinate( + x, + y, + width * 2, + radian + Math.PI * 0.5, + ); + this.moveTo(x0, y0); + this.lineTo(p1.x, p1.y); + this.lineTo(p2.x, p2.y); + this.lineTo(x1, y1); + this.lineTo(p4.x, p4.y); + this.lineTo(p3.x, p3.y); + this.closePath(); + }; + CanvasRenderingContext2D.prototype.arrow = function ( + startX, + startY, + endX, + endY, + controlPoints, + ) { + var dx = endX - startX; + var dy = endY - startY; + var len = Math.sqrt(dx * dx + dy * dy); + var sin = dy / len; + var cos = dx / len; + var a = []; + a.push(0, 0); + for (var i = 0; i < controlPoints.length; i += 2) { + var x = controlPoints[i]; + var y = controlPoints[i + 1]; + a.push(x < 0 ? len + x : x, y); + } + a.push(len, 0); + for (var i = controlPoints.length; i > 0; i -= 2) { + var x = controlPoints[i - 2]; + var y = controlPoints[i - 1]; + a.push(x < 0 ? len + x : x, -y); + } + a.push(0, 0); + for (var i = 0; i < a.length; i += 2) { + var x = a[i] * cos - a[i + 1] * sin + startX; + var y = a[i] * sin + a[i + 1] * cos + startY; + if (i === 0) this.moveTo(x, y); + else this.lineTo(x, y); + } + }; + })(DGraph), + (function (DGraph) { + function Thumbnail(stage, miniMapConfig) { + (this.padding = 0), + (this.visible = false), + (this.container = null), + (this.canvas = null), + (this.viewBox = null), + (this.thumbnailCtx = null), + (this.width = miniMapConfig.width), + (this.height = miniMapConfig.height); + this.timer = null; + this.sceneChilds = 0; + this.initialize = function () { + var container = document.createElement("div"); + container.className = "thumbnail"; + container.style.cssText = `position:absolute;width:${this.width}px;height:${this.height}px;border:1px solid#e1d2d2;background-color:transparent;overflow:hidden;user-select:none;-webkit-user-drag:none;-webkit-tap-highlight-color:rgba(0,0,0 0);`; + var viewportCvs = document.createElement("canvas"); + viewportCvs.style.cssText = + "width:100%;height:100%;background-color:transparent;cursor:not-allowed;pointer-events:none;"; + viewportCvs.width = this.width * stage.pixelRatio; + viewportCvs.height = this.height * stage.pixelRatio; + var miniMapContainer = miniMapConfig.container; + var viewContainer = document.getElementById(miniMapContainer); + if (viewContainer) { + viewContainer.appendChild(container); + container.style.left = 0; + container.style.top = 0; + } else { + stage.canvas.parentNode.style.position = "relative"; + stage.canvas.parentNode.appendChild(container); + container.style.right = 0; + container.style.bottom = 0; + } + container.appendChild(viewportCvs); + var viewBox = document.createElement("div"); + viewBox.style.cssText = `position:absolute;top:0px;left:0px;width:${this.width * stage.pixelRatio}px;height:${this.height * stage.pixelRatio}px;border:1px solid ${miniMapConfig.viewColor};background:rgba(110,110,250,0.3);cursor:not-allowed;pointer-events:none;`; + + container.appendChild(viewBox); + this.container = container; + this.canvas = viewportCvs; + this.thumbnailCtx = viewportCvs.getContext("2d"); + this.viewBox = viewBox; + this.addThumbEvents(); + }; + this.addThumbEvents = function () { + var self = this; + this.container.addEventListener("click", function (event) { + var mouse = { left: event.offsetX, top: event.offsetY }; + var bound = stage.boundCache; + var scene = stage.childs[0]; + var left = bound.left * (self.width / bound.width) + mouse.left; + var top = bound.top * (self.height / bound.height) + mouse.top; + var scaleX = stage.width / (scene.scaleX * bound.width); + var scaleY = stage.height / (scene.scaleY * bound.height); + left /= scaleX; + top /= scaleY; + var translateX = left / ((self.width / stage.width) * scene.scaleX); + var translateY = + top / ((self.height / stage.height) * scene.scaleY); + scene.setCenter(translateX, translateY); + stage.repaint(); + }); + }; + this.destory = function () { + this.visible = false; + this.sceneChilds = 0; + clearInterval(this.timer); + if (this.container) { + this.container.removeEventListener("click", function () {}, false); + this.container.parentNode.removeChild(this.container); + this.container = null; + this.canvas = null; + this.thumbnailCtx = null; + } + }; + this.show = function () { + var self = this; + if (!this.visible && this.container == null) { + this.initialize(); + this.visible = true; + self.timer = setInterval(function () { + var currentChildCount = stage.childs[0].childs.length; + if ( + currentChildCount == 0 || + self.sceneChilds != currentChildCount + ) { + self.update(); + self.paint(); + } + self.sceneChilds = currentChildCount; + }, 1000); + } + }; + (this.update = function () { + var bound = stage.getBound(); + if (bound == null || bound.width < 0) { + return null; + } + var scene = stage.childs[0]; + var width = this.width * stage.pixelRatio; + var height = this.height * stage.pixelRatio; + var scale = Math.min(width / bound.width, height / bound.height); + var ctx = this.thumbnailCtx; + ctx.save(), + ctx.clearRect(0, 0, width, height), + scene.save(), + scene.centerAndZoom(scale, scale, ctx), + scene.paintNodes(ctx); + scene.restore(); + ctx.restore(); + }), + (this.paint = function () { + if (!this.visible) { + return; + } + var bound = stage.boundCache; + if (bound == null) { + return; + } + var scene = stage.childs[0]; + var translate = scene.getOffsetTranslate(); + var left = + translate.translateX * + (this.width / stage.width) * + scene.scaleX, + top = + translate.translateY * + (this.height / stage.height) * + scene.scaleY, + scaleX = stage.width / (scene.scaleX * bound.width), + scaleY = stage.height / (scene.scaleY * bound.height); + (left *= scaleX), (top *= scaleY); + left += bound.left * (this.width / bound.width); + top += bound.top * (this.height / bound.height); + var rectWidth = Math.round(this.width * scaleX); + var rectHeight = Math.round(this.height * scaleY); + left = Math.round(left); + top = Math.round(top); + this.viewBox.style.width = `${rectWidth}px`; + this.viewBox.style.height = `${rectHeight}px`; + this.viewBox.style.left = `${-left}px`; + this.viewBox.style.top = `${-top}px`; + }); + } + function Stage(container, miniMapConfig) { + var self = this; + self.canvasElements = new Map(); + self.canvasContexts = new Map(); + self.container = container; + let viewCanvas = createCanvas("elements"); + let mouseCanvas = createCanvas("mouse"); + self.offCanvas = document.createElement("canvas"); + self.canvas = mouseCanvas; + self.viewGraphics = viewCanvas.getContext("2d"); + self.graphics = self.offCanvas.getContext("2d"); + self.mouseGraphics = mouseCanvas.getContext("2d"); + self.pixelRatio = getPixelRatio(viewCanvas); + setCanvasSize(); + mouseCanvas.oncontextmenu = function (e) { + if (e.button == 2) { + e.preventDefault(); + return false; + } + }; + function mouseEvent(event) { + var mouse = DGraph.util.getEventPosition(event), + offset = DGraph.util.getOffsetPosition(self.canvas); + return ( + (mouse.offsetLeft = mouse.pageX - offset.left), + (mouse.offsetTop = mouse.pageY - offset.top), + (mouse.x = mouse.offsetLeft * self.pixelRatio), + (mouse.y = mouse.offsetTop * self.pixelRatio), + (mouse.target = null), + mouse + ); + } + function createCanvas(canvasId) { + const canvas = document.createElement("canvas"); + canvas.setAttribute("class", `graphvis-${canvasId}`); + canvas.style.position = "absolute"; + const contextOptions = { + preserveDrawingBuffer: false, + antialias: false, + }; + self.canvasElements.set(canvasId, canvas); + self.canvasContexts.set( + canvasId, + canvas.getContext("2d", contextOptions), + ); + self.container.appendChild(canvas); + return canvas; + } + function setCanvasSize() { + var width = self.container.offsetWidth || 200; + var height = self.container.offsetHeight || 100; + self.canvasElements.forEach((_canvas, _canvasId) => { + _canvas.style.width = width + "px"; + _canvas.style.height = height + "px"; + _canvas.width = Math.round(width * self.pixelRatio); + _canvas.height = Math.round(height * self.pixelRatio); + }); + self.offCanvas.width = Math.round(width * self.pixelRatio); + self.offCanvas.height = Math.round(height * self.pixelRatio); + } + function getPixelRatio(canvas) { + let ctx = canvas.getContext("2d"); + if (ctx === undefined) { + return; + } + var numerator = 1; + if (typeof window !== "undefined") { + numerator = window.devicePixelRatio || 1.0; + } + var denominator = + ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || + 1.0; + return numerator / denominator; + } + function mouseover(event) { + document.onselectstart = function () { + return false; + }; + var _mouseEvent = mouseEvent(event); + self.dispatchEventToScenes("mouseover", _mouseEvent); + self.dispatchEvent("mouseover", _mouseEvent); + } + function mouseout(event) { + self.needRepaint = false; + document.onselectstart = function () { + return true; + }; + var _mouseEvent = mouseEvent(event); + self.dispatchEventToScenes("mouseout", _mouseEvent); + self.dispatchEvent("mouseout", _mouseEvent); + } + function mousedown(event) { + self.mouseDown = true; + var _mouseEvent = mouseEvent(event); + self.mouseDownX = _mouseEvent.x; + self.mouseDownY = _mouseEvent.y; + self.dispatchEventToScenes("mousedown", _mouseEvent); + self.dispatchEvent("mousedown", _mouseEvent); + } + function mouseup(event) { + self.mouseDown = false; + if (self.dragging) { + self.dragEndMouseUp = true; + setTimeout(function () { + self.dragEndMouseUp = false; + self.dragging = false; + self.paint(); + }, 200); + } + var _mouseEvent = mouseEvent(event); + self.dispatchEventToScenes("mouseup", _mouseEvent); + self.dispatchEvent("mouseup", _mouseEvent); + self.needRepaint = false; + self.clearSelectArea(); + self.thumbnail.visible && self.thumbnail.update(); + } + function mousemove(event) { + var _mouseEvent = mouseEvent(event); + if (self.mouseDown && (1 == event.buttons || 0 == event.buttons)) { + (_mouseEvent.dx = _mouseEvent.x - self.mouseDownX), + (_mouseEvent.dy = _mouseEvent.y - self.mouseDownY); + if ( + Math.sqrt( + _mouseEvent.dx * _mouseEvent.dx + + _mouseEvent.dy * _mouseEvent.dy, + ) > 2 + ) { + self.dragging = true; + self.dispatchEventToScenes("mousedrag", _mouseEvent); + self.dispatchEvent("mousedrag", _mouseEvent); + } + } else { + if (!self.staticMode) { + self.dispatchEventToScenes("mousemove", _mouseEvent); + self.dispatchEvent("mousemove", _mouseEvent); + } + } + } + function click(event) { + event.preventDefault(); + if (self.dragEndMouseUp) { + return false; + } + clearTimeout(self.clickTimer); + self.clickTimer = setTimeout(function () { + var _mouseEvent = mouseEvent(event); + self.dispatchEventToScenes("click", _mouseEvent); + self.dispatchEvent("click", _mouseEvent); + }, 200); + } + function dblclick(event) { + event.preventDefault(); + clearTimeout(self.clickTimer); + var _mouseEvent = mouseEvent(event); + self.dispatchEventToScenes("dbclick", _mouseEvent); + self.dispatchEvent("dbclick", _mouseEvent); + } + function mousewheel(event) { + if (!self.wheelZoom) { + return; + } + self.needRepaint = true; + self.wheeling = self.smoothWheelMode; + var wheelDelta = + null == event.wheelDelta ? event.detail : event.wheelDelta; + if (wheelDelta > 0) { + self.zoomOut(self.wheelZoom); + } + else { + self.zoomIn(self.wheelZoom); + } + if (event.preventDefault) { + event.preventDefault(); + } else { + window.event, (event.returnValue = false); + } + setTimeout(function () { + self.wheeling = false; + self.needRepaint = false; + self.paint(); + }, 300); + } + function touchmove(event) { + event.preventDefault(); + if (self.istouch) { + if (event.touches && event.touches.length < 2) { + mousemove(touchEventToMouseEvent(event, "mousemove")); + } else { + event.preventDefault(); + const touches = event.touches; + var now = touches; + var start = self.touchStart; + if ( + getDistance(now[0], now[1]) < getDistance(start[0], start[1]) + ) { + self.zoomIn(0.95); + } else if ( + getDistance(now[0], now[1]) > getDistance(start[0], start[1]) + ) { + self.zoomOut(0.95); + } + self.touchStart = now; + } + } else { + mousemove(touchEventToMouseEvent(event, "mousemove")); + } + function getDistance(p1, p2) { + var x = p2.pageX - p1.pageX, + y = p2.pageY - p1.pageY; + return Math.sqrt(x * x + y * y); + } + } + function touchstart(event) { + self.istouch = true; + if (event.touches && event.touches.length < 2) { + mousedown(touchEventToMouseEvent(event, "mousedown")); + } else { + const touches = event.touches; + if (touches.length >= 2) { + self.touchStart = touches; + } + } + } + function touchend(event) { + if (self.istouch) { + self.istouch = false; + self.touchStart = null; + mouseup(touchEventToMouseEvent(event, "mousedown")); + } + } + function touchEventToMouseEvent(event, eventType) { + if (!event.changedTouches || event.changedTouches.length != 1) { + return false; + } + var te = event.changedTouches[0]; + var clientX = te.clientX, + clientY = te.clientY, + screenX = te.screenX, + screenY = te.screenY; + var simEvent = new MouseEvent(eventType, { + clientX: clientX, + clientY: clientY, + screenX: screenX, + screenY: screenY, + button: 0, + buttons: 0, + }); + return simEvent; + } + function initEvents(cavs) { + DGraph.util.isIE || !window.addEventListener + ? ((cavs.onmouseout = mouseout), + (cavs.onmouseover = mouseover), + (cavs.onmousedown = mousedown), + (cavs.onmouseup = mouseup), + (cavs.onmousemove = mousemove), + (cavs.onclick = click), + (cavs.ondblclick = dblclick), + (cavs.onmousewheel = mousewheel), + (cavs.touchstart = touchstart), + (cavs.touchmove = touchmove), + (cavs.touchend = touchend)) + : (cavs.addEventListener("mouseout", mouseout), + cavs.addEventListener("mouseover", mouseover), + cavs.addEventListener("mousedown", mousedown), + cavs.addEventListener("mouseup", mouseup), + cavs.addEventListener("mousemove", mousemove), + cavs.addEventListener("click", click), + cavs.addEventListener("dblclick", dblclick), + cavs.addEventListener("touchstart", touchstart), + cavs.addEventListener("touchmove", touchmove), + cavs.addEventListener("touchend", touchend), + DGraph.util.isFirefox + ? cavs.addEventListener("DOMMouseScroll", mousewheel) + : cavs.addEventListener("mousewheel", mousewheel)); + window.addEventListener("resize", function (event) { + self.resize(); + }); + window.addEventListener( + "keydown", + function (event) { + self.dispatchEventToScenes( + "keydown", + DGraph.util.cloneEvent(event), + false, + ); + }, + true, + ); + window.addEventListener( + "keyup", + function (event) { + self.dispatchEventToScenes( + "keyup", + DGraph.util.cloneEvent(event), + false, + ); + }, + true, + ); + } + (this.initialize = function (canvas) { + (this.childs = []), + (this.fps = -50), + (this.messageBus = new DGraph.util.MessageBus()), + (this.wheelZoom = null), + (this.mouseDownX = 0), + (this.mouseDownY = 0), + (this.mouseDown = false), + (this.needRepaint = false), + (this.istouch = false), + (this.touchStart = null), + (this.wheeling = false); + this.dragging = false; + this.dragEndMouseUp = false; + this.clickTimer = null; + this.openDragHideEffect = false; + this.smoothWheelMode = true; + this.staticMode = false; + this.requestAnimateId = null; + this.loadingAnimateId = null; + this.boundCache = null; + this.showDetailScale = 0.5; + this.thumbnail = new Thumbnail(this, miniMapConfig); + initEvents(canvas); + (this.resize = function () { + var nowWidth = this.container.offsetWidth; + var nowHeight = this.container.offsetHeight; + if (nowWidth == 0 || nowHeight == 0) { + return false; + } + setCanvasSize(); + this.paint(); + }), + (this.dispatchEventToScenes = function ( + eventName, + mouse, + _needRepaint = true, + ) { + this.needRepaint = _needRepaint; + this.childs.forEach(function (item) { + if (item.visible == true) { + var event = item[eventName + "Handler"]; + if (null == event) return null; + event.call(item, mouse); + } + }); + }), + (this.add = function (sence) { + for (var i = 0; i < this.childs.length; i++) + if (this.childs[i] === sence) return; + sence.addTo(this), this.childs.push(sence); + }), + (this.remove = function (sence) { + if (null == sence) return null; + for (var i = 0; i < this.childs.length; i++) + if (this.childs[i] === sence) + return ( + (sence.stage = null), + (this.childs = DGraph.util.removeFromArray( + this.childs, + this.childs[i], + )), + this + ); + return this; + }), + (this.clear = function () { + this.childs = []; + }), + (this.addEventListener = function (eventName, eventFunction) { + var self = this, + fn = function (event) { + eventFunction.call(self, event); + }; + return this.messageBus.subscribe(eventName, fn), this; + }), + (this.removeEventListener = function (eventName) { + this.messageBus.unsubscribe(eventName); + }), + (this.removeAllEventListener = function () { + this.messageBus = new DGraph.util.MessageBus(); + }), + (this.dispatchEvent = function (eventName, event) { + return this.messageBus.publish(eventName, event), this; + }); + }), + this.initialize(self.canvas); + var eventNames = [ + "click", + "dbclick", + "mousedown", + "mouseup", + "mouseover", + "mouseout", + "mousemove", + "mousedrag", + "mousewheel", + "keydown", + "keyup", + ]; + eventNames.forEach(function (eventName) { + self[eventName] = function (event) { + null != event + ? this.addEventListener(eventName, event) + : this.dispatchEvent(eventName); + }; + }); + (this.getPagePosition = function (x, y) { + var self = this; + if (!this.childs || this.childs.length == 0) { + return { pageX: 0, pageY: 0 }; + } + var secne = this.childs && this.childs[0]; + var secneOffset = secne.getOffsetTranslate(); + var offset = DGraph.util.getOffsetPosition(self.canvas); + var offsetLeft = + ((x + secneOffset.translateX) * secne.scaleX) / self.pixelRatio; + var offsetTop = + ((y + secneOffset.translateY) * secne.scaleY) / self.pixelRatio; + return { + pageX: offsetLeft + offset.left, + pageY: offsetTop + offset.top, + }; + }), + (this.getImageUrlData = function (config) { + var bound = this.getBound(); + if (bound == null || bound.width < 0) { + return null; + } + var imageWidth = config.width || 8000; + var imageHeight = config.height || 8000; + var type = config.type; + var rectWidth = bound.width, + rectHeight = bound.height; + var paddingX = 0, + paddingY = 0; + if ( + bound.leftNode.elementType == "node" && + bound.rightNode.elementType == "node" + ) { + paddingX = + bound.leftNode.radius * bound.leftNode.scaleX + + bound.rightNode.radius * bound.rightNode.scaleX; + paddingY = + bound.topNode.radius * bound.topNode.scaleY + + bound.bottomNode.radius * bound.bottomNode.scaleY; + } + rectWidth += paddingX + 50; + rectHeight += paddingY + 50; + var exportCanvas = document.createElement("canvas"); + var width = Math.min(imageWidth || 10000, rectWidth); + var height = Math.min(imageHeight || 10000, rectHeight); + exportCanvas.width = width; + exportCanvas.height = height; + var scale = Math.min(width / rectWidth, height / rectHeight); + if (Math.max(rectWidth, rectHeight) < 500) { + exportCanvas.width = Math.max( + Math.max(rectWidth, rectHeight), + 500, + ); + exportCanvas.height = Math.max( + Math.max(rectWidth, rectHeight), + 500, + ); + scale = 1; + } + var exportContext2d = exportCanvas.getContext("2d"); + if (config.background) { + exportContext2d.save(); + exportContext2d.fillStyle = config.background || "#fff"; + exportContext2d.fillRect(0, 0, width, height); + exportContext2d.restore(); + } + if (config.textWatermark) { + exportContext2d.save(); + this.paintTextWatermark(exportContext2d, width, height, { + content: config.textWatermark.content || " ", + angle: config.textWatermark.angle || -30, + alpha: config.textWatermark.alpha || 0.1, + fontStyle: config.textWatermark.fontStyle || "normal", + fontSize: config.textWatermark.fontSize || 60, + fontFamliy: config.textWatermark.fontFamliy || "Arial", + fontColor: config.textWatermark.color || "#666", + }); + exportContext2d.restore(); + } + var scene = this.childs[0]; + scene.save(), + scene.centerAndZoom(scale, scale, exportContext2d), + scene.repaint(exportContext2d, true), + scene.restore(); + var imageObj = null; + try { + imageObj = exportCanvas.toDataURL("image/" + (type || "png"), 1); + } catch (m) {} + return imageObj; + }), + (this.paintTextWatermark = function ( + context, + width, + height, + textWatermark, + ) { + let { + content, + alpha, + angle, + fontSize, + fontStyle, + fontFamliy, + fontColor, + } = textWatermark; + context.font = `${fontStyle}${fontSize}px ${fontFamliy}`; + var contentWidth = Math.ceil(context.measureText(content).width); + context.fillStyle = fontColor || "#666"; + context.textBaseline = "middle"; + context.textAlign = "center"; + context.globalAlpha = alpha || 0.1; + var hSpace = Math.round(contentWidth * 0.6); + var wSpace = contentWidth + Math.round(width / 50); + let heightNumber = Math.ceil(height / hSpace) + 5; + let widthNumber = Math.ceil(width / wSpace) + 5; + for (let i = 0; i < heightNumber; i++) { + for (let w = 0; w < widthNumber; w++) { + context.save(); + context.translate(w * wSpace, i * hSpace); + context.rotate((angle * Math.PI) / 180); + context.fillText(content, 0, 0); + context.restore(); + } + } + context.globalAlpha = 1; + }), + (this.saveAsLocalImage = function (config) { + var type = config.type ? config.type : "png"; + var imageData = this.getImageUrlData(config); + if (imageData != null) { + var fixtype = function (type) { + type = type.toLowerCase().replace(/jpg/i, "jpeg"); + var r = type.match(/png|jpeg|bmp|gif/); + if (r) { + r = r[0]; + } + return "image" + r; + }; + imageData = imageData.replace( + fixtype(type), + "image/octet-stream", + ); + let a = document.createElement("a"); + let event = new MouseEvent("click"); + a.download = + (config.fileName || "picture" + new Date().getTime()) + + "." + + type; + a.href = imageData; + a.dispatchEvent(event); + } + }), + (this.paint = function () { + if (this.childs.length > 0) { + this.graphics.save(); + this.graphics.clearRect(0, 0, this.width, this.height); + this.childs.forEach(function (scene) { + scene.visible && scene.repaint(self.graphics); + }); + this.graphics.restore(); + self.viewGraphics.clearRect(0, 0, this.width, this.height); + self.viewGraphics.drawImage(self.offCanvas, 0, 0); + } + }), + (this.repaint = function () { + if (this.fps > 0) { + this.paint(); + } else if (this.fps < 0) { + if (this.needRepaint) { + this.needRepaint = false; + this.paint(); + } + } else { + this.needRepaint = false; + } + }), + (this.paintSelectRect = function (x, y, width, height) { + this.clearSelectArea(); + this.mouseGraphics.save(); + this.mouseGraphics.beginPath(), + this.mouseGraphics.rect( + x * this.pixelRatio, + y * this.pixelRatio, + width, + height, + ), + this.mouseGraphics.closePath(), + (this.mouseGraphics.strokeStyle = "rgba(0,225,0,0.8)"), + (this.mouseGraphics.fillStyle = "rgba(0,225,0,0.1)"), + this.mouseGraphics.fill(), + this.mouseGraphics.stroke(), + this.mouseGraphics.restore(); + }), + (this.paintSelectPolygon = function (x, y, points) { + this.clearSelectArea(); + this.mouseGraphics.save(); + this.mouseGraphics.beginPath(); + this.mouseGraphics.moveTo(x * this.pixelRatio, y * this.pixelRatio); + for (var i = 1; i < points.length; i++) { + this.mouseGraphics.lineTo( + points[i][0] * this.pixelRatio, + points[i][1] * this.pixelRatio, + ); + } + this.mouseGraphics.closePath(), + (this.mouseGraphics.strokeStyle = "rgba(0,225,0,0.8)"), + (this.mouseGraphics.fillStyle = "rgba(0,225,0,0.1)"), + this.mouseGraphics.fill(), + this.mouseGraphics.stroke(), + this.mouseGraphics.restore(); + }), + (this.clearSelectArea = function () { + this.mouseGraphics.clearRect(0, 0, this.width, this.height); + }), + (this.showLoading = function ( + loadingText = "loading", + percent = 0.2, + ) { + this.clearSelectArea(); + var ctx = this.mouseGraphics; + var radius = 80, + PI = Math.PI, + PI2 = PI / 2, + M2PI = 2 * PI; + ctx.save(); + ctx.translate(this.width / 2, this.height / 2); + ctx.fillStyle = "rgba(240,240,240,1)"; + ctx.strokeStyle = "rgba(200,200,200,0.8)"; + ctx.lineWidth = 10; + ctx.beginPath(); + ctx.arc(0, 0, radius, 0, Math.PI * 2, false); + ctx.fill(); + ctx.stroke(); + ctx.closePath(); + ctx.strokeStyle = "rgba(120,120,240,0.9)"; + ctx.lineWidth = 10; + ctx.lineCap = "round"; + ctx.beginPath(); + ctx.arc(0, 0, radius, -PI2, M2PI * percent - PI2, false); + ctx.stroke(); + ctx.closePath(); + ctx.font = "normal 16px KaiTi"; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = `rgba(50,50,50,1)`; + ctx.fillText(loadingText, 0, 0); + ctx.restore(); + }), + (this.hideLoading = function () { + this.clearSelectArea(); + }), + (this.zoom = function (scale) { + this.childs.forEach(function (child) { + child.visible && child.zoom(scale); + }); + }), + (this.zoomOut = function (scale, mouseEvent) { + this.childs.forEach(function (child) { + child.visible && child.zoomOut(scale); + }); + }), + (this.zoomIn = function (scale, mouseEvent) { + this.childs.forEach(function (child) { + child.visible && child.zoomIn(scale); + }); + }), + (this.centerAndZoom = function () { + this.childs.forEach(function (child) { + child.visible && child.centerAndZoom(); + }); + }), + (this.setCenter = function (x, y) { + var self = this; + this.childs.forEach(function (child) { + var translateX = x - self.canvas.width / 2, + translateY = y - self.canvas.height / 2; + (child.translateX = -translateX), + (child.translateY = -translateY); + }); + }), + (this.getBound = function () { + var bound = { + left: Number.MAX_VALUE, + right: Number.MIN_VALUE, + top: Number.MAX_VALUE, + bottom: Number.MIN_VALUE, + }; + if (this.childs.length == 0 || this.childs[0].childs == 0) { + return null; + } + this.childs.forEach(function (child) { + var _cbound = child.getElementsBound(); + _cbound.left < bound.left && + ((bound.left = _cbound.left), + (bound.leftNode = _cbound.leftNode)), + _cbound.top < bound.top && + ((bound.top = _cbound.top), + (bound.topNode = _cbound.topNode)), + _cbound.right > bound.right && + ((bound.right = _cbound.right), + (bound.rightNode = _cbound.rightNode)), + _cbound.bottom > bound.bottom && + ((bound.bottom = _cbound.bottom), + (bound.bottomNode = _cbound.bottomNode)); + }); + (bound.width = bound.right - bound.left), + (bound.height = bound.bottom - bound.top); + this.boundCache = bound; + return bound; + }); + var requestFn = (() => { + let lastTime = 0; + return typeof requestAnimationFrame !== "undefined" + ? requestAnimationFrame + : function (fn) { + const now = Date.now(); + const delay = Math.min(16, Math.max(0, 16 - (now - lastTime))); + const elapsed = (lastTime = now + delay); + return setTimeout(() => fn(elapsed), delay); + }; + })(); + var cancelFn = + typeof cancelAnimationFrame !== "undefined" + ? cancelAnimationFrame + : function (id) { + return clearTimeout(id); + }; + function requestAnimFrame(fn, context) { + return requestFn((elapsed) => { + fn.call(context, elapsed); + }); + } + function cancelAnimFrame(id) { + if (id) { + cancelFn(id); + } + } + function render() { + if (0 == self.fps) { + setTimeout(arguments.callee, 60); + } else { + cancelAnimFrame(self.requestAnimateId); + self.repaint(); + self.requestAnimateId = requestAnimFrame(render); + } + } + render(); + } + (Stage.prototype = { + get width() { + return this.canvas.width; + }, + get height() { + return this.canvas.height; + }, + set mode(mode) { + this.childs.forEach(function (item) { + item.mode = mode; + }); + }, + }), + (DGraph.Stage = Stage); + })(DGraph), + (function (DGraph) { + function Scene(_stage) { + var self = this; + (this.initialize = function () { + Scene.prototype.initialize.apply(this, arguments), + (this.messageBus = new DGraph.util.MessageBus()), + (this.elementType = "scene"), + (this.childs = []), + (this.zIndexMap = {}), + (this.zIndexArray = []), + (this.backgroundColor = "255,255,255"), + (this.visible = true), + (this.alpha = 0), + (this.scaleX = 1), + (this.scaleY = 1), + (this.scaleRange = [0.05, 10]), + (this.pickMode = "node-first"); + (this.mode = DGraph.SceneMode.normal), + (this.translate = true), + (this.translateX = 0), + (this.translateY = 0), + (this.lastTranslateX = 0), + (this.lastTranslateY = 0), + (this.mouseDown = false), + (this.mouseDownX = null), + (this.mouseDownY = null), + (this.mouseDownEvent = null), + (this.dragable = true), + (this.areaSelect = false), + (this.selectBoxType = "rect"), + (this.operations = []), + (this.selectedElements = []), + (this.polygonPoints = null), + (this.polygonCoords = null), + (this.dragMouseIndex = 0); + this.displayElements = { nodes: [], links: [], groups: [] }; + this.serializedProperties = [ + "translateX", + "translateY", + "lastTranslatedX", + "lastTranslatedY", + "scaleX", + "scaleY", + ]; + (this.setBackground = function (background) { + this.background = background; + }), + (this.addTo = function (stage) { + this.stage !== stage && null != stage && (this.stage = stage); + }); + }), + this.initialize(); + if (null != _stage) { + _stage.add(this); + this.addTo(_stage); + } + (this.show = function () { + this.visible = true; + }), + (this.hide = function () { + this.visible = false; + }), + (this.paint = function (ctx, allPaint) { + ctx.save(); + ctx.scale(this.scaleX, this.scaleY); + if (true == this.translate) { + var b = this.getOffsetTranslate(ctx); + ctx.translate(b.translateX, b.translateY); + } + if (allPaint) { + this.paintAllChilds(ctx); + } else { + this.paintChilds(ctx); + } + ctx.restore(), + ctx.save(), + this.paintOperations(ctx, this.operations), + ctx.restore(); + }), + (this.repaint = function (ctx, allPaint) { + this.visible && this.paint(ctx, allPaint); + }), + (this.paintBackgroud = function (ctx) { + if (null != this.background) { + ctx.drawImage( + this.background, + 0, + 0, + ctx.canvas.width, + ctx.canvas.height, + ); + } else { + ctx.beginPath(), + (ctx.fillStyle = `rgba(${this.backgroundColor},${this.alpha})`), + ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height), + ctx.closePath(); + } + }), + (this.getDisplayedElements = function () { + var self = this; + self.displayElements = { nodes: [], edges: [], groups: [] }; + let smallFlag = + _stage.wheeling || this.scaleX <= this.scaleRange[0] + ? true + : false; + if (_stage.openDragHideEffect) { + smallFlag = _stage.dragging || smallFlag; + } + self.displayElements.groups = this.zIndexMap[DGraph.Group_zIndex]; + var nodes = this.zIndexMap[DGraph.Node_zIndex] || []; + var length = nodes.length; + var node = null; + var offset = this.getOffsetTranslate(); + var showFlag = false; + var showEdges = new Set(); + for (var i = 0; i < length; i++) { + node = nodes[i]; + showFlag = this.nodeInView(node, offset); + if (showFlag) { + self.displayElements.nodes.push(node); + if (!smallFlag) { + (node.outLinks || []).forEach((link) => { + link.visible && showEdges.add(link); + }); + (node.inLinks || []).forEach((link) => { + link.visible && showEdges.add(link); + }); + } + } + } + self.displayElements.edges = Array.from(showEdges); + return self.displayElements; + }), + (this.paintChilds = function (ctx) { + var self = this; + var displayElements = self.getDisplayedElements(); + var smallFlag = this.scaleX <= this.scaleRange[0]; + var selectedNodes = [], + selectedEdges = []; + (displayElements.groups || []).forEach((group) => { + self.paintElement(ctx, group); + }); + (displayElements.edges || []).forEach((edge) => { + if (edge.isMouseOver || edge.selected) { + selectedEdges.push(edge); + } else { + self.paintElement(ctx, edge, smallFlag); + } + }); + selectedEdges.forEach((edge) => { + self.paintElement(ctx, edge, smallFlag); + }); + (displayElements.nodes || []).forEach((node) => { + if (node.isMouseOver || node.selected) { + selectedNodes.push(node); + } else { + self.paintElement(ctx, node, smallFlag); + } + }); + selectedNodes.forEach((edge) => { + self.paintElement(ctx, edge, smallFlag); + }); + }), + (this.paintElement = function (ctx, element, smallFlag) { + if (element.visible) { + ctx.save(); + if (element.transformAble) { + var position = element.getCenterLocation(); + ctx.translate(position.x, position.y); + ctx.rotate((element.rotate * Math.PI) / 180); + ctx.scale(element.scaleX, element.scaleY); + } + if (element.isMouseOver && !smallFlag) { + element.paintMouseover(ctx); + } + if ( + element.selected || + element.showSelected || + element.isMouseOver + ) { + element.hideText = false; + } else { + element.hideText = false; + } + element.paint(ctx, smallFlag); + ctx.restore(); + } + }), + (this.paintAllChilds = function (ctx) { + for (var i = 0; i < this.zIndexArray.length; i++) { + var d = this.zIndexArray[i], + e = this.zIndexMap[d]; + for (var f = 0; f < e.length; f++) { + var ele = e[f]; + if (ele.visible == true) { + ctx.save(); + if (ele.transformAble) { + var pos = ele.getCenterLocation(); + ctx.translate(pos.x, pos.y); + ctx.rotate((ele.rotate * Math.PI) / 180); + ctx.scale(ele.scaleX, ele.scaleY); + } + if (ele.isMouseOver) { + ele.paintMouseover(ctx); + } + ele.hideText = false; + ele.paint(ctx, false); + ctx.restore(); + } + } + } + }), + (this.paintNodes = function (ctx) { + var nodes = this.zIndexMap[DGraph.Node_zIndex] || []; + if (nodes.length == 0) { + return false; + } + ctx.save(); + ctx.scale(this.scaleX, this.scaleY); + if (true == this.translate) { + var offset = this.getOffsetTranslate(ctx); + ctx.translate(offset.translateX, offset.translateY); + } + var pos = { x: 0, y: 0 }; + nodes.forEach(function (node) { + pos = node.getCenterLocation(); + ctx.save(); + ctx.translate(pos.x, pos.y); + ctx.rotate((node.rotate * Math.PI) / 180); + ctx.scale(node.scaleX, node.scaleY); + node.paint(ctx, true); + ctx.restore(); + }); + ctx.restore(); + }), + (this.getOffsetTranslate = function (context) { + var width = this.stage.width, + height = this.stage.height; + if (context != null) { + width = context.canvas.width; + height = context.canvas.height; + } + var offsetX = width / this.scaleX / 2, + offsetY = height / this.scaleY / 2; + return { + translateX: this.translateX + (offsetX - offsetX * this.scaleX), + translateY: this.translateY + (offsetY - offsetY * this.scaleY), + }; + }), + (this.nodeInView = function (node, offset) { + if (node == null || !node.visible) { + return false; + } + var offsetX = (node.x + offset.translateX) * this.scaleX; + if (offsetX > this.stage.width) { + return false; + } + var offsetY = (node.y + offset.translateY) * this.scaleY; + if (offsetY > this.stage.height) { + return false; + } + var width = offsetX + node.width * this.scaleX; + if (0 > width) { + return false; + } + var height = offsetY + node.height * this.scaleY; + if (0 > height) { + return false; + } + return true; + }), + (this.isVisiable = function (ele, offset) { + if (ele == null || !ele.visible) { + return false; + } + if (ele instanceof DGraph.Link) { + if (ele.source.visible && ele.target.visible) { + return true; + } + return false; + } + return this.nodeInView(ele, offset); + }), + (this.paintOperations = function (ctx, eles) { + for (var i = 0; i < eles.length; i++) { + eles[i](ctx); + } + }), + (this.addOperation = function (operat) { + return this.operations.push(operat), this; + }), + (this.clearOperations = function () { + return (this.operations = []), this; + }), + (this.getElementByXY = function (x, y) { + var elements = []; + var element = null; + var length = 0; + var pickSort = ["nodes", "edges", "groups"]; + if (this.pickMode != "node-first") { + pickSort = ["edges", "nodes", "groups"]; + } + for (var i = 0; i < pickSort.length; i++) { + elements = this.displayElements[pickSort[i]] || []; + length = elements.length; + for (var j = 0; j < length; j++) { + element = elements[j]; + if (element.visible && element.isInBound(x, y)) { + return element; + } + element = null; + } + } + return element; + }), + (this.add = function (ele) { + this.childs.push(ele), + null == this.zIndexMap[ele.zIndex] && + ((this.zIndexMap[ele.zIndex] = []), + this.zIndexArray.push(ele.zIndex), + this.zIndexArray.sort(function (zindexA, zindexB) { + return zindexA - zindexB; + })), + this.zIndexMap["" + ele.zIndex].push(ele); + }), + (this.remove = function (ele) { + this.childs = DGraph.util.removeFromArray(this.childs, ele); + var zIndexEles = this.zIndexMap[ele.zIndex]; + if (zIndexEles) { + this.zIndexMap[ele.zIndex] = DGraph.util.removeFromArray( + zIndexEles, + ele, + ); + ele.removeHandler(this); + } + }), + (this.clear = function () { + (this.childs = []), + (this.operations = []), + (this.zIndexArray = []), + (this.zIndexMap = {}); + }), + (this.addToSelected = function (ele) { + if (this.selectedElements.indexOf(ele) == -1) { + this.selectedElements.push(ele); + } + }), + (this.cancleAllSelected = function (ele) { + for (var i = 0; i < this.selectedElements.length; i++) { + this.selectedElements[i].unselectedHandler(ele); + } + this.selectedElements = []; + }), + (this.notInSelectedNodes = function (ele) { + for (var i = 0; i < this.selectedElements.length; i++) { + if (ele === this.selectedElements[i]) return false; + } + return true; + }), + (this.removeFromSelected = function (ele) { + for (var i = 0; i < this.selectedElements.length; i++) { + var selEle = this.selectedElements[i]; + if (ele === selEle) { + this.selectedElements = DGraph.util.removeFromArray( + this.selectedElements, + selEle, + ); + } + } + }), + (this.toSceneEvent = function (ele) { + var newELe = DGraph.util.clone(ele); + newELe.x /= this.scaleX; + newELe.y /= this.scaleY; + if (true == this.translate) { + var offset = this.getOffsetTranslate(); + newELe.x -= offset.translateX; + newELe.y -= offset.translateY; + } + return ( + null != newELe.dx && + ((newELe.dx /= this.scaleX), (newELe.dy /= this.scaleY)), + null != this.currentElement && + (newELe.target = this.currentElement), + (newELe.scene = this), + newELe + ); + }), + (this.selectElement = function (event) { + var self = this; + var selEle = self.getElementByXY(event.x, event.y); + if (null != selEle) { + event.target = selEle; + selEle.mousedownHander(event); + selEle.selectedHandler(event); + if (self.notInSelectedNodes(selEle)) { + event.ctrlKey || self.cancleAllSelected(), + self.addToSelected(selEle); + } else { + event.ctrlKey && + (selEle.unselectedHandler(), this.removeFromSelected(selEle)); + for (var i = 0; i < this.selectedElements.length; i++) { + this.selectedElements[i].selectedHandler(event); + } + } + } else { + event.ctrlKey || self.cancleAllSelected(); + } + this.currentElement = selEle; + }), + (this.mousedownHandler = function (event) { + var evt = this.toSceneEvent(event); + (this.mouseDownX = evt.x), + (this.mouseDownY = evt.y), + (this.mouseDownEvent = evt); + if ( + (this.mouseDown = true && this.mode == DGraph.SceneMode.normal) + ) { + this.selectElement(evt), + (null == this.currentElement || + this.currentElement instanceof DGraph.Link) && + true == this.translate && + ((this.lastTranslateX = this.translateX), + (this.lastTranslateY = this.translateY)); + } else { + if (this.mode == DGraph.SceneMode.drag && true == this.translate) + return ( + (this.lastTranslateX = this.translateX), + (this.lastTranslateY = this.translateY) + ); + this.mode == DGraph.SceneMode.select && this.selectElement(evt); + } + self.dispatchEvent("mousedown", evt); + }), + (this.mouseupHandler = function (event) { + self.clearOperations(); + var evt = this.toSceneEvent(event); + null != this.currentElement && + ((evt.target = self.currentElement), + this.currentElement.mouseupHandler(evt)); + this.dispatchEvent("mouseup", evt), (this.mouseDown = false); + if (this.areaSelect) { + this.areaSelect = false; + this.boxSelectEndHandle(); + } + }), + (this.dragElements = function (event) { + if (null != this.currentElement && this.currentElement.dragable) { + var e = DGraph.util.clone(event); + for (var i = 0; i < this.selectedElements.length; i++) { + var ele = this.selectedElements[i]; + if (ele.dragable) { + (e.target = ele), ele.mousedragHandler(e); + } + } + } + }), + (this.mousedragHandler = function (event) { + var mouse = this.toSceneEvent(event); + if (this.mode == DGraph.SceneMode.normal) { + if ( + null == this.currentElement || + this.currentElement instanceof DGraph.Link + ) { + this.dragable && + this.translate && + ((this.translateX = this.lastTranslateX + mouse.dx), + (this.translateY = this.lastTranslateY + mouse.dy)); + } else { + this.dragable && this.dragElements(mouse); + } + } else if (this.mode == DGraph.SceneMode.drag) { + this.dragable && + this.translate && + ((this.translateX = this.lastTranslateX + mouse.dx), + (this.translateY = this.lastTranslateY + mouse.dy)); + } else if (this.mode == DGraph.SceneMode.select) { + if (null != this.currentElement) { + this.currentElement.dragable && this.dragElements(mouse); + } else { + this.stage.needRepaint = false; + this.areaSelect = true; + this.selectBoxType == "rect" + ? this.selectRectHandle(mouse) + : this.selectPolygonHandle(mouse); + } + } else { + this.dispatchEvent("mousedrag", mouse); + } + }), + (this.boxSelectEndHandle = function () { + var ele = null; + var pickSort = ["nodes", "groups"]; + var nodes = []; + for (var i = 0; i < pickSort.length; i++) { + var elements = this.displayElements[pickSort[i]] || []; + var length = elements.length; + for (var j = 0; j < length; j++) { + ele = elements[j]; + if ( + DGraph.util.isInPolygon([ele.cx, ele.cy], this.polygonCoords) + ) { + ele.selectedHandler(event), this.addToSelected(ele); + nodes.push(ele); + } + } + } + this.boxSelectedNodes = nodes; + this.polygonPoints = null; + this.polygonCoords = null; + this.dragMouseIndex = 0; + this.stage.repaint(); + }), + (this.selectPolygonHandle = function (event) { + var left = event.offsetLeft, + top = event.offsetTop; + this.dragMouseIndex++; + if (this.polygonPoints == null) { + this.polygonPoints = [[left, top]]; + this.polygonCoords = [[event.x, event.y]]; + } else { + if (this.dragMouseIndex % 10 == 0) { + this.polygonPoints.push([left, top]); + this.polygonCoords.push([event.x, event.y]); + } + } + this.stage.paintSelectPolygon(left, top, this.polygonPoints); + }), + (this.selectRectHandle = function (event) { + var left = event.offsetLeft, + top = event.offsetTop, + mouseLeft = this.mouseDownEvent.offsetLeft, + mouseTop = this.mouseDownEvent.offsetTop, + x = left >= mouseLeft ? mouseLeft : left, + y = top >= mouseTop ? mouseTop : top, + dx = Math.abs(event.dx) * this.scaleX, + dy = Math.abs(event.dy) * this.scaleY; + this.stage.paintSelectRect(x, y, dx, dy); + x = + event.x >= this.mouseDownEvent.x + ? this.mouseDownEvent.x + : event.x; + y = + event.y >= this.mouseDownEvent.y + ? this.mouseDownEvent.y + : event.y; + var width = x + Math.abs(event.dx), + height = y + Math.abs(event.dy); + this.polygonCoords = [ + [x, y], + [width, y], + [width, height], + [x, height], + ]; + }), + (this.mousemoveHandler = function (event) { + var mouseEvent = self.toSceneEvent(event); + var targetEle = self.getElementByXY(mouseEvent.x, mouseEvent.y); + if (null != targetEle) { + if ( + self.mouseOverelement && + self.mouseOverelement !== targetEle + ) { + mouseEvent.target = targetEle; + self.mouseOverelement.mouseoutHandler(mouseEvent); + } + self.mouseOverelement = targetEle; + if (!targetEle.isMouseOver) { + mouseEvent.target = targetEle; + targetEle.mouseoverHandler(mouseEvent); + self.dispatchEvent("mouseover", mouseEvent); + } else { + mouseEvent.target = targetEle; + } + } else { + if (self.mouseOverelement) { + mouseEvent.target = targetEle; + self.mouseOverelement.mouseoutHandler(mouseEvent); + self.mouseOverelement = null; + self.dispatchEvent("mouseout", mouseEvent); + } else { + mouseEvent.target = null; + self.dispatchEvent("mousemove", mouseEvent); + } + } + }), + (this.mouseoverHandler = function (a) { + var b = this.toSceneEvent(a); + this.dispatchEvent("mouseover", b); + }), + (this.mouseoutHandler = function (a) { + var b = this.toSceneEvent(a); + this.dispatchEvent("mouseout", b); + }), + (this.clickHandler = function (a) { + var b = this.toSceneEvent(a); + this.currentElement && + ((b.target = this.currentElement), + this.currentElement.clickHandler(b)), + this.dispatchEvent("click", b); + }), + (this.dbclickHandler = function (a) { + var b = this.toSceneEvent(a); + this.currentElement + ? ((b.target = this.currentElement), + this.currentElement.dbclickHandler(b)) + : this.cancleAllSelected(), + this.dispatchEvent("dbclick", b); + }), + (this.keydownHandler = function (event) { + this.dispatchEvent("keydown", event); + }), + (this.keyupHandler = function (event) { + this.dispatchEvent("keyup", event); + }), + (this.addEventListener = function (a, b) { + var c = this, + d = function (a) { + b.call(c, a); + }; + return this.messageBus.subscribe(a, d), this; + }), + (this.removeEventListener = function (event) { + this.messageBus.unsubscribe(event); + }), + (this.removeAllEventListener = function () { + this.messageBus = new DGraph.util.MessageBus(); + }), + (this.dispatchEvent = function (a, b) { + return this.messageBus.publish(a, b), this; + }); + var eventNames = + "click,dbclick,mousedown,mouseup,mouseover,mouseout,mousemove,mousedrag,keydown,keyup".split( + ",", + ), + self = this; + return ( + eventNames.forEach(function (eventName) { + self[eventName] = function (event) { + null != event + ? this.addEventListener(eventName, event) + : this.dispatchEvent(eventName); + }; + }), + (this.zoom = function (scaleX, scaleY) { + null != scaleX && 0 != scaleX && (this.scaleX = scaleX), + null != scaleY && 0 != scaleY && (this.scaleY = scaleY); + }), + (this.zoomOut = function (scale) { + if (this.scaleX <= this.scaleRange[1]) { + 0 != scale && + (null == scale && (scale = 0.8), + (this.scaleX /= scale), + (this.scaleY /= scale)); + } + }), + (this.zoomIn = function (scale) { + if (this.scaleX >= this.scaleRange[0]) { + 0 != scale && + (null == scale && (scale = 0.8), + (this.scaleX *= scale), + (this.scaleY *= scale)); + } + + }), + (this.getBound = function () { + return { + left: 0, + top: 0, + right: this.stage.width, + bottom: this.stage.height, + width: this.stage.width, + height: this.stage.height, + }; + }), + (this.getElementsBound = function () { + if (this.childs.length == 0) { + return null; + } + return this.getBoundWithElemets(this.childs); + }), + (this.getBoundWithElemets = function (elements) { + var bound = { + left: Number.MAX_VALUE, + right: -Number.MAX_VALUE, + top: Number.MAX_VALUE, + bottom: -Number.MAX_VALUE, + }; + elements + .filter(function (it) { + return it.visible && it instanceof DGraph.Node; + }) + .forEach(function (node) { + if (bound.left > node.x) { + bound.left = node.x; + bound.leftNode = node; + } + if (bound.right < node.x + node.width) { + bound.right = node.x + node.width; + bound.rightNode = node; + } + if (bound.top > node.y) { + bound.top = node.y; + bound.topNode = node; + } + if (bound.bottom < node.y + node.height) { + bound.bottom = node.y + node.height; + bound.bottomNode = node; + } + }); + if (bound.leftNode.parentContainer) { + bound.left = bound.left - bound.leftNode.parentContainer.padding; + } + if (bound.rightNode.parentContainer) { + bound.right = + bound.right + bound.rightNode.parentContainer.padding; + } + if (bound.bottomNode.parentContainer) { + bound.bottom = + bound.bottom + bound.bottomNode.parentContainer.padding; + } + if (bound.topNode.parentContainer) { + bound.top = + bound.top - + bound.topNode.parentContainer.padding - + bound.topNode.parentContainer.headerHeight; + } + bound.width = bound.right - bound.left; + bound.height = bound.bottom - bound.top; + return bound; + }), + (this.translateToCenter = function (context) { + var bound = this.getElementsBound(); + if (bound != null) { + var translateX = 0, + translateY = 0; + if (context) { + + translateX = + context.canvas.width / 2 - (bound.left + bound.right) / 2; + translateY = + context.canvas.height / 2 - (bound.top + bound.bottom) / 2; + } else { + + (translateX = + this.stage.width / 2 - (bound.left + bound.right) / 2), + (translateY = + this.stage.height / 2 - (bound.top + bound.bottom) / 2); + } + this.translateX = translateX; + this.translateY = translateY; + } + }), + (this.setCenter = function (x, y) { + var offsetX = x - this.stage.width / 2; + var offsetY = y - this.stage.height / 2; + this.translateX = -offsetX; + this.translateY = -offsetY; + }), + (this.centerAndZoom = function (scaleX, scaleY, context) { + this.translateToCenter(context); + if (null == scaleX || null == scaleY) { + var bound = this.getElementsBound(); + if (bound != null) { + var width = bound.right - bound.left; + var height = bound.bottom - bound.top; + var scaleX = 1, + scaleY = 1; + if (context) { + scaleX = context.canvas.width / width; + scaleY = context.canvas.height / height; + } else { + scaleX = this.stage.width / width; + scaleY = this.stage.height / height; + } + var scale = Math.min(scaleX, scaleY); + if (scale > 1) { + scale = 1; + } + this.zoom(scale, scale); + } + } else { + this.zoom(scaleX, scaleY); + } + }), + (this.getCenterLocation = function () { + var self = this; + return { x: self.stage.width / 2, y: self.stage.height / 2 }; + }), + self + ); + } + Scene.prototype = new DGraph.Element(); + DGraph.Scene = Scene; + })(DGraph), + (function (DGraph) { + function DisplayElement() { + (this.initialize = function () { + DisplayElement.prototype.initialize.apply(this, arguments), + (this.elementType = "displayElement"), + (this.x = 0), + (this.y = 0), + (this.width = 40), + (this.height = 40), + (this.radius = 20); + this.size = 40; + (this.visible = true), + (this.alpha = 1), + (this.rotate = 0), + (this.scaleX = 1), + (this.scaleY = 1), + (this.strokeColor = "22,255,22"), + (this.borderColor = "22,255,22"), + (this.fillColor = "22,255,22"), + (this.shadow = false), + (this.shadowBlur = 20), + (this.shadowColor = "rgba(20,200,20,0.5)"), + (this.shadowOffsetX = 3), + (this.shadowOffsetY = 6), + (this.transformAble = false), + (this.animate = false), + (this.zIndex = 0); + this.properties = {}; + }), + this.initialize(), + (this.paint = function (ctx) { + ctx.beginPath(), + ctx.rect( + -this.width / 2, + -this.height / 2, + this.width, + this.height, + ); + if (this.fillColor) { + (ctx.fillStyle = `rgba(${this.fillColor},${this.alpha})`), + ctx.fill(); + } + (ctx.strokeStyle = `rgba(${this.strokeColor},${this.alpha})`), + ctx.stroke(), + ctx.closePath(); + }), + (this.getLocation = function () { + return { x: this.x, y: this.y }; + }), + (this.setLocation = function (x, y) { + this.x = x; + this.y = y; + return this; + }), + (this.getCenterLocation = function () { + return { x: this.x + this.width / 2, y: this.y + this.height / 2 }; + }), + (this.setCenterLocation = function (x, y) { + this.x = x - this.width / 2; + this.y = y - this.height / 2; + return this; + }), + (this.getSize = function () { + return { width: this.width, height: this.height }; + }), + (this.setSize = function (width, height) { + this.width = Number(width) || 40; + this.height = Number(height) || 40; + this.raduis = Math.round(this.width / 2); + return this; + }), + (this.getBound = function () { + return { + left: this.x - this.borderWidth, + top: this.y - this.borderWidth, + right: this.x + this.width + this.borderWidth, + bottom: this.y + this.height + this.borderWidth, + width: this.width, + height: this.height, + }; + }), + (this.setBound = function (x, y, width, height) { + this.setLocation(x, y); + this.setSize(width, height); + return this; + }), + (this.getPosition = function (textPosition) { + var pos, + bound = this.getBound(); + switch (textPosition) { + case "Top_Left": + pos = { x: bound.left, y: bound.top }; + break; + case "Top_Center": + pos = { x: this.cx, y: bound.top }; + break; + case "Top_Right": + pos = { x: bound.right, y: bound.top }; + break; + case "Middle_Left": + pos = { x: bound.left, y: this.cy }; + break; + case "Middle_Center": + pos = { x: this.cx, y: this.cy }; + break; + case "Middle_Right": + pos = { x: bound.right, y: this.cy }; + break; + case "Bottom_Left": + pos = { x: bound.left, y: bound.bottom }; + break; + case "Bottom_Center": + pos = { x: this.cx, y: bound.bottom }; + break; + case "Bottom_Right": + pos = { x: bound.right, y: bound.bottom }; + break; + default: + pos = { x: this.cx, y: this.cy }; + break; + } + return pos; + }); + } + function InteractiveElement() { + (this.initialize = function () { + InteractiveElement.prototype.initialize.apply(this, arguments), + (this.elementType = "interactiveElement"), + (this.dragable = false), + (this.selected = false), + (this.showSelected = false), + (this.isMouseOver = false), + (this.selectedLocation = null), + (this.showBackGround = false), + (this.animate = false); + }), + this.initialize(), + (this.paintMouseover = function (ctx) { + this.showSelected = true; + }), + (this.isInBound = function (x, y) { + if (this.shape == "rect") { + var width = this.width * this.scaleX; + var height = this.height * this.scaleY; + if (this.rotate == 0) { + return ( + x > this.cx - width / 2 && + x < this.cx + width / 2 && + y > this.cy - height / 2 && + y < this.cy + height / 2 + ); + } else { + var cpx = this.cx, + cpy = this.cy; + var angle = -((this.rotate * Math.PI) / 180); + var x0 = + (x - cpx) * Math.cos(angle) - + (y - cpy) * Math.sin(angle) + + cpx; + var y0 = + (x - cpx) * Math.sin(angle) + + (y - cpy) * Math.cos(angle) + + cpy; + return ( + x0 > this.cx - width / 2 && + x0 < this.cx + width / 2 && + y0 > this.cy - height / 2 && + y0 < this.cy + height / 2 + ); + } + } + else { + console.log(this.radius) + return ( + (x - this.cx) * (x - this.cx) + (y - this.cy) * (y - this.cy) < + this.radius * this.scaleX * this.radius * this.scaleX + ); + } + }), + (this.selectedHandler = function () { + (this.selected = true), + (this.selectedLocation = { x: this.x, y: this.y }); + }), + (this.unselectedHandler = function () { + (this.selected = false), (this.selectedLocation = null); + }), + (this.dbclickHandler = function (event) { + this.dispatchEvent("dbclick", event); + }), + (this.clickHandler = function (event) { + this.dispatchEvent("click", event); + }), + (this.mousedownHander = function (a) { + this.dispatchEvent("mousedown", a); + }), + (this.mouseupHandler = function (event) { + this.dispatchEvent("mouseup", event); + }), + (this.mouseoverHandler = function (event) { + (this.isMouseOver = true), this.dispatchEvent("mouseover", event); + }), + (this.mousemoveHandler = function (event) { + this.dispatchEvent("mousemove", event); + }), + (this.mouseoutHandler = function (event) { + (this.isMouseOver = false), + (this.showSelected = false), + this.dispatchEvent("mouseout", event); + }), + (this.mousedragHandler = function (event) { + var x = this.selectedLocation.x + event.dx, + y = this.selectedLocation.y + event.dy; + this.setLocation(x, y); + this.dispatchEvent("mousedrag", event); + }), + (this.addEventListener = function (eventName, fucn) { + var self = this, + event = function (e) { + fucn.call(self, e); + }; + return ( + this.messageBus || + (this.messageBus = new DGraph.util.MessageBus()), + this.messageBus.subscribe(eventName, event), + this + ); + }), + (this.dispatchEvent = function (eventName, fucn) { + return this.messageBus + ? (this.messageBus.publish(eventName, fucn), this) + : null; + }), + (this.removeEventListener = function (event) { + this.messageBus.unsubscribe(event); + }), + (this.removeAllEventListener = function () { + this.messageBus = new DGraph.util.MessageBus(); + }); + var self = this; + var eventNames = [ + "click", + "dbclick", + "mousedown", + "mouseup", + "mouseover", + "mouseout", + "mousemove", + "mousedrag", + ]; + eventNames.forEach(function (eventName) { + self[eventName] = function (event) { + null != event + ? this.addEventListener(eventName, event) + : this.dispatchEvent(eventName); + }; + }); + } + (DisplayElement.prototype = new DGraph.Element()), + Object.defineProperties(DisplayElement.prototype, { + cx: { + get: function () { + return this.x + this.width / 2; + }, + set: function (x) { + this.x = x - this.width / 2; + }, + }, + cy: { + get: function () { + return this.y + this.height / 2; + }, + set: function (y) { + this.y = y - this.height / 2; + }, + }, + radius: { + get: function () { + return Math.round(this.width / 2); + }, + set: function (r) { + this.width = r * 2; + }, + }, + }), + (InteractiveElement.prototype = new DisplayElement()), + (DGraph.DisplayElement = DisplayElement), + (DGraph.InteractiveElement = InteractiveElement); + })(DGraph), + (function (DGraph) { + function Node(name) { + (this.initialize = function (name) { + Node.prototype.initialize.apply(this, arguments), + (this.elementType = "node"), + (this.zIndex = DGraph.Node_zIndex), + (this.text = name), + (this.font = "normal 13px Arial"), + (this.fontColor = "20,20,20"), + (this.borderWidth = 0), + (this.borderColor = "20,255,20"), + (this.borderAlpha = 1), + (this.selectedBorderColor = "255,0,0"), + (this.selectedBorderWidth = 10), + (this.selectedBorderAlpha = 0.7), + (this.borderRadius = 0), + (this.shadowColor = "20,255,20"), + (this.shadowBlur = 10), + (this.dragable = true), + (this.textPosition = "Middle_Center"), + (this.textOffsetX = 0), + (this.textOffsetY = 0), + (this.transformAble = true), + (this.inLinks = null), + (this.outLinks = null); + this.width = 60; + this.height = 60; + (this.labelBackGround = null), + (this.labelBorderWidth = 0), + (this.labelBorderColor = "255,255,255"); + this.fillStyle = null; + this.textLines = []; + this.fontWidth = 18; + this.icon = { + font: "bold 20px Consolas", + text: null, + color: "255,255,255", + left: -this.width, + top: this.height / 2, + }; + this.textCache = { + sigleWidth: 0, + maxWidth: 0, + lineCount: 0, + font: null, + }; + }), + this.initialize(name), + (this.drawNodeImg = function (ctx, img, x, y, r) { + + if (img && img.width) { + + if (this.shape == "circle") { + r = r > 2 ? r : 2; + if (r < img.width / 2) { + ctx.arc(x + r, y + r, r, 0, 2 * Math.PI); + ctx.clip(); + ctx.drawImage(img, x, y, 2 * r, 2 * r); + } else { + ctx.drawImage( + img, + -img.width / 2, + -img.height / 2, + img.width, + img.height, + ); + } + } else if (this.shape == "rect") { + ctx.drawImage(img, x, y, this.width, this.height); + } else { + ctx.drawImage( + img, + -img.width / 2, + -img.height / 2, + img.width, + img.height, + ); + } + } + }), + (this.paint = function (ctx, onlyShape) { + if (this.drawNode && typeof this.drawNode === "function") { + if (this.showBackGround) { + ctx.save(), ctx.beginPath(); + ctx.rect( + -this.width / 2 - 5, + -this.height / 2 - 5, + this.width + 10, + this.height + 10, + ); + ctx.closePath(); + ctx.fillStyle = "rgba(220,220,220,0.5)"; + ctx.fill(); + ctx.restore(); + } + this.drawNode(ctx, onlyShape); + return false; + } + this.drawOriginalNode(ctx, onlyShape); + }), + (this.drawOriginalNode = function (ctx, onlyShape) { + this.drawShape(ctx, onlyShape); + if (this.image && !onlyShape) { + var globleAlpha = ctx.globalAlpha; + ctx.save(); + ctx.globalAlpha = this.alpha; + this.drawNodeImg( + ctx, + this.image, + -this.width / 2, + -this.height / 2, + this.width / 2, + ); + ctx.globalAlpha = globleAlpha; + ctx.restore(); + } + if (!onlyShape && !this.hideText) { + if (this.icon && this.icon.text) { + this.paintIcon(ctx); + } + this.paintText(ctx), this.paintTipText(ctx); + this.paintHeadTip(ctx); + } + }), + (this.drawShape = function (ctx, onlyShape) { + ctx.save(); + ctx.beginPath(); + this.paintShape(ctx); + if (!onlyShape) { + if (this.lineDash && this.lineDash.length > 1) { + ctx.setLineDash(this.lineDash); + } + if ( + (this.showSelected || this.selected) && + this.selectedBorderWidth > 0 + ) { + ctx.lineWidth = this.borderWidth + this.selectedBorderWidth; + ctx.strokeStyle = `rgba(${this.selectedBorderColor},${this.alpha * this.selectedBorderAlpha})`; + ctx.stroke(); + } + if ( + !this.selected && + !this.showSelected && + this.borderWidth > 0 + ) { + ctx.lineWidth = this.borderWidth; + ctx.strokeStyle = this.strokeStyle + ? this.strokeStyle + : `rgba(${this.borderColor},${this.alpha * this.borderAlpha})`; + ctx.stroke(); + } + if (this.fillColor || onlyShape) { + this.paintShadow(ctx); + ctx.fillStyle = this.fillStyle + ? this.fillStyle + : `rgba(${this.fillColor},${this.alpha})`; + ctx.fill(); + } + } else { + if (this.fillStyle) { + ctx.fillStyle = this.fillStyle; + ctx.fill(); + } else { + if (this.fillColor) { + ctx.fillStyle = `rgba(${this.fillColor},${this.alpha})`; + ctx.fill(); + } else { + ctx.lineWidth = this.borderWidth; + ctx.strokeStyle = `rgba(${this.borderColor},${this.alpha})`; + ctx.stroke(); + } + } + } + ctx.restore(); + }), + (this.paintShape = function (ctx) { + switch (this.shape) { + case "rect": + if (!this.borderRadius) { + ctx.rect( + -this.width / 2, + -this.height / 2, + this.width, + this.height, + ); + } else { + var width = this.width; + var height = this.height; + var r2d = Math.PI / 180; + var r = this.borderRadius; + var x = -width / 2, + y = -height / 2; + if (width - 2 * r < 0) { + r = width / 2; + } + if (height - 2 * r < 0) { + r = height / 2; + } + ctx.moveTo(x + r, y); + ctx.lineTo(x + width - r, y); + ctx.arc(x + width - r, y + r, r, r2d * 270, r2d * 360, false); + ctx.lineTo(x + width, y + height - r); + ctx.arc(x + width - r, y + height - r, r, 0, r2d * 90, false); + ctx.lineTo(x + r, y + height); + ctx.arc(x + r, y + height - r, r, r2d * 90, r2d * 180, false); + ctx.lineTo(x, y + r); + ctx.arc(x + r, y + r, r, r2d * 180, r2d * 270, false); + ctx.closePath(); + } + break; + case "ellipse": + ctx.ellipse( + 0, + 0, + this.radius, + this.radius / 2, + 0, + 0, + 2 * Math.PI, + ); + break; + case "square": + ctx.rect( + -this.radius, + -this.radius, + this.radius * 2, + this.radius * 2, + ); + break; + case "triangle": + ctx.moveTo(0, -this.radius); + ctx.lineTo(-this.radius, this.radius); + ctx.lineTo(this.radius, this.radius); + ctx.closePath(); + break; + case "star": + var r = this.radius * 2 * 0.41; + for (var n = 0; n < 10; n++) { + var radius = n % 2 === 0 ? r * 1.3 : r * 0.5; + ctx.lineTo( + radius * Math.sin((n * 2 * Math.PI) / 10), + 0.1 * r - radius * Math.cos((n * 2 * Math.PI) / 10), + ); + } + ctx.closePath(); + break; + case "polygon": + var degree = (2 * Math.PI) / 6; + for (var i = 0; i < 6; i++) { + var x = Math.cos(i * degree); + var y = Math.sin(i * degree); + ctx.lineTo(x * this.radius, y * this.radius); + } + ctx.closePath(); + break; + case "circle": + this.radius = this.radius < 2 ? 2 : this.radius; + ctx.arc(0, 0, this.radius, 0, Math.PI * 2, false); + + break; + default: + this.radius = this.radius < 2 ? 2 : this.radius; + ctx.arc(0, 0, this.radius, 0, 2 * Math.PI, true); + break; + } + }), + (this.paintTipText = function (ctx) { + if (this.tipText) { + var b = this.alarmColor || "0,250,0", + c = this.alarmAlpha || 0.8 * this.alpha; + ctx.font = this.alarmFont || "10px Consolas"; + var d = ctx.measureText(this.tipText).width + 6, + e = ctx.measureText("田").width + 6, + f = this.width / 2 - d / 2, + g = -this.height / 2 - e - 8; + (ctx.strokeStyle = `rgba(${b},${c})`), + (ctx.fillStyle = `rgba(${b},${c})`), + ctx.DGraphRoundRect( + this.width / 3, + -this.height / 2, + d + 5, + e, + 8, + ), + ctx.stroke(), + ctx.fill(), + (ctx.fillStyle = `rgba(250,250,250,${this.alpha})`), + ctx.fillText( + this.tipText, + this.width / 3 + 5, + -this.height / 2 + 12, + ); + } + }), + (this.paintText = function (ctx) { + var self = this; + // this.showlabel = true; + var label; + label = this.text = this.label; + // if (this.showlabel) { + // label = this.text = this.label; + // } else { + // label = this.text = this.label; + // return; + // } + if (label) { + var pos; + ctx.save(); + ctx.font = this.font; + var singleTextWidth = 0; + ctx.fillStyle = `rgba(${this.fontColor},${this.alpha})`; + if (this.wrapText == true) { + singleTextWidth = ctx.measureText("田").width; + var textArr = this.buildTextArray(label); + var maxWidth = 0; + ctx.textAlign = "center"; + textArr.forEach(function (text, i) { + text = String(text); + maxWidth = Math.max( + maxWidth, + ctx.measureText(text.replace(/ /g, "田")).width, + ); + ctx.fillText( + text, + 0, + -((singleTextWidth / 2) * textArr.length) + + (i + 1) * singleTextWidth, + ); + }); + this.width = this.width-0.5; + // maxWidth + 8 < this.width ? this.width : maxWidth + 8; + } else { + this.processLabel(ctx); + singleTextWidth = this.textCache.sigleWidth; + var lineCount = this.textLines.length; + var labelHeight = lineCount * singleTextWidth; + if (this.labelBackGround) { + pos = this.getTextPostion( + this.textPosition, + this.textCache.maxWidth, + singleTextWidth, + ); + ctx.save(); + ctx.lineWidth = 1; + ctx.strokeStyle = `rgba(${this.labelBorderColor},${this.alpha})`; + ctx.fillStyle = `rgba(${this.labelBackGround},${this.alpha})`; + ctx.DGraphRoundRect( + pos.x - 5, + pos.y - singleTextWidth * 1.45, + this.textCache.maxWidth + 10, + labelHeight + singleTextWidth + (lineCount == 1 ? 0 : 4), + 5, + ); + ctx.stroke(), ctx.fill(), ctx.restore(); + } + if (this.labelBorderWidth > 0) { + ctx.lineWidth = this.labelBorderWidth; + ctx.strokeStyle = `rgba(${this.labelBorderColor},${this.alpha})`; + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + } + if (lineCount == 1) { + pos = this.getTextPostion( + this.textPosition, + this.textCache.maxWidth, + singleTextWidth, + ); + this.labelBorderWidth > 0 + ? ctx.strokeText(this.textLines[0], pos.x, pos.y) + : null; + ctx.fillText(this.textLines[0], pos.x, pos.y - 1); + } else { + for (let i = 0; i < lineCount; i++) { + pos = this.getTextPostion( + this.textPosition, + ctx.measureText(this.textLines[i]).width, + singleTextWidth, + ); + this.labelBorderWidth > 0 + ? ctx.strokeText( + this.textLines[i], + pos.x, + pos.y + i * singleTextWidth + (i == 0 ? 0 : 2 * i), + ) + : null; + ctx.fillText( + this.textLines[i], + pos.x, + pos.y - 1 + i * singleTextWidth + (i == 0 ? 0 : 2 * i), + ); + } + } + } + ctx.restore(); + } + }), + (this.processLabel = function (ctx) { + this.textLines = String(this.label).split("\n"); + if ( + this.textCache.sigleWidth == 0 || + this.textCache.lineCount != this.textLines.length || + this.textCache.font != this.font + ) { + this.textCache.sigleWidth = ctx.measureText("田").width; + this.textCache.font = this.font; + this.textCache.lineCount = this.textLines.length; + if (this.textLines.length == 1) { + this.textCache.maxWidth = ctx.measureText( + this.textLines[0], + ).width; + } else { + for (let i = 0; i < this.textLines.length; i++) { + this.textCache.maxWidth = Math.max( + ctx.measureText(this.textLines[i]).width, + this.textCache.maxWidth, + ); + } + } + } + }), + (this.buildTextArray = function (label) { + var length = String(label).length; + var textArr = []; + if (length < 5) { + textArr.push(label); + } else if (length >= 5 && length <= 9) { + textArr.push(label.substring(0, 4)); + textArr.push(label.substring(4)); + } else if (length > 9 && length <= 13) { + textArr.push(label.substring(0, 4)); + textArr.push(label.substring(4, 9)); + textArr.push(label.substring(9)); + } else { + textArr.push(label.substring(0, 4)); + textArr.push(label.substring(4, 9)); + textArr.push(label.substring(9, 12) + ".."); + } + return textArr; + }); + (this.paintShadow = function (ctx) { + if (this.showShadow && this.selected) { + (ctx.shadowBlur = this.shadowBlur), + (ctx.shadowColor = `rgba(${this.shadowColor},${this.alpha * 0.9})`), + (ctx.shadowOffsetX = 0), + (ctx.shadowOffsetY = 0); + } + }), + (this.paintIcon = function (ctx) { + ctx.save(), + (ctx.fillStyle = `rgba(${this.icon.color},${this.alpha})`); + (ctx.font = this.icon.font), + ctx.fillText( + this.icon.text, + this.icon.left || 5, + this.icon.top || 5, + ), + ctx.restore(); + }), + (this.paintHeadTip = function (ctx) { + if (this.headTipText) { + var radius = this.headTipRadius || 0; + var padding = 16; + ctx.save(); + ctx.font = this.headTipFont || "normal 13px Arial"; + var width = ctx.measureText(this.headTipText).width + padding; + var height = ctx.measureText("田").width + padding - 2; + radius = radius > height / 2 ? height / 2 : radius; + ctx.translate(-width / 2, -height - this.height / 2 - 6); + ctx.beginPath(); + ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2); + ctx.lineTo(width / 2.0 + 4, height); + ctx.lineTo(width / 2.0, height + 6); + ctx.lineTo(width / 2.0 - 4, height); + ctx.lineTo(radius, height); + ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI); + ctx.lineTo(0, radius); + ctx.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2); + ctx.lineTo(width - radius, 0); + ctx.arc( + width - radius, + radius, + radius, + (Math.PI * 3) / 2, + Math.PI * 2, + ); + ctx.lineTo(width, height - radius); + ctx.closePath(); + ctx.fillStyle = `rgba(${this.headTipColor || "250,50,50"},${this.alpha})`; + ctx.fill(); + ctx.translate(width / 2, height / 2); + ctx.textBaseline = "middle"; + ctx.textAlign = "center"; + ctx.fillStyle = `rgba(${this.headTipFontColor || "250,250,250"},${this.alpha})`; + ctx.fillText(this.headTipText, 0, 0); + ctx.restore(); + } + }), + (this.getTextPostion = function ( + textPosition, + textWidth, + textHeight, + ) { + var position = null; + return ( + null == textPosition || "Bottom_Center" == textPosition + ? (position = { + x: -this.width / 2 + (this.width - textWidth) / 2, + y: this.height / 2 + textHeight, + }) + : "Top_Center" == textPosition + ? (position = { + x: -this.width / 2 + (this.width - textWidth) / 2, + y: -this.height / 2 - textHeight / 2, + }) + : "Top_Right" == textPosition + ? (position = { + x: this.width / 2, + y: -this.height / 2 - textHeight / 2, + }) + : "Top_Left" == textPosition + ? (position = { + x: -this.width / 2 - textWidth, + y: -this.height / 2 - textHeight / 2, + }) + : "Bottom_Right" == textPosition + ? (position = { + x: this.width / 2, + y: this.height / 2 + textHeight, + }) + : "Bottom_Left" == textPosition + ? (position = { + x: -this.width / 2 - textWidth, + y: this.height / 2 + textHeight, + }) + : "Middle_Center" == textPosition + ? (position = { + x: + -this.width / 2 + + (this.width - textWidth) / 2, + y: textHeight / 2, + }) + : "Middle_Right" == textPosition + ? (position = { + x: this.width / 2, + y: textHeight / 2, + }) + : "Middle_Left" == textPosition && + (position = { + x: -this.width / 2 - textWidth, + y: textHeight / 2, + }), + null == position + ? (position = { + x: -this.width / 2 + (this.width - textWidth) / 2, + y: this.height / 2 + textHeight, + }) + : position, + null != this.textOffsetX && (position.x += this.textOffsetX), + null != this.textOffsetY && (position.y += this.textOffsetY), + position + ); + }), + (this.setImage = function (imgS) { + if (null == imgS) throw new Error("image is null!"); + var self = this; + var image = DGraph.imgStore[imgS]; + if (image != null) { + self.image = image; + return; + } + if ("string" == typeof imgS) { + var img = new Image(); + img.setAttribute("crossOrigin", "Anonymous"); + img.src = imgS; + img.onload = function () { + self.image = img; + }; + DGraph.imgStore[imgS] = img; + } else { + (this.image = imgS), this.setSize(imgS.width, imgS.height); + } + }), + (this.removeHandler = function (a) { + var self = this; + this.outLinks && + (this.outLinks.forEach(function (link) { + link.source === self && a.remove(link); + }), + (this.outLinks = null)); + this.inLinks && + (this.inLinks.forEach(function (link) { + link.target === self && a.remove(link); + }), + (this.inLinks = null)); + }); + } + (Node.prototype = new DGraph.InteractiveElement()), (DGraph.Node = Node); + })(DGraph), + (function (DGraph) { + function mergeLine(source, target) { + var lines = []; + if (null == source || null == target) return lines; + if (source.outLinks && target.inLinks) + for (var i = 0; i < source.outLinks.length; i++) + for ( + var outLink = source.outLinks[i], j = 0; + j < target.inLinks.length; + j++ + ) { + var inLink = target.inLinks[j]; + outLink === inLink && lines.push(inLink); + } + return lines; + } + function findLines(source, target, twoDirect) { + if (twoDirect != null && twoDirect == true) { + var outLines = mergeLine(source, target), + inLines = mergeLine(target, source), + allLines = outLines.concat(inLines); + return allLines; + } + return mergeLine(source, target); + } + function findAllLines(line) { + var lines = findLines(line.source, line.target); + return (lines = lines.filter(function (_line) { + return line !== _line; + })); + } + function calLineNum(source, target, twoDirect) { + return findLines(source, target, twoDirect).length; + } + function Link(source, target, label) { + (this.initialize = function (source, target, label) { + Link.prototype.initialize.apply(this, arguments); + this.elementType = "link"; + this.zIndex = DGraph.Link_zIndex; + (this.text = label), + (this.source = source), + (this.target = target), + this.source && + null == this.source.outLinks && + (this.source.outLinks = []), + this.source && + null == this.source.inLinks && + (this.source.inLinks = []), + this.target && + null == this.target.inLinks && + (this.target.inLinks = []), + this.target && + null == this.target.outLinks && + (this.target.outLinks = []), + null != this.source && this.source.outLinks.push(this), + null != this.target && this.target.inLinks.push(this), + this.caculateIndex(), + (this.font = "normal 13px Arial"), + (this.fontColor = "120,120,120"), + (this.lineWidth = 2), + (this.lineJoin = "round"), + (this.showShadow = false); + (this.shadowColor = "10,250,10"), + (this.selectedColor = "10,10,230"), + (this.selectedAlpha = 1); + this.background = null; + (this.transformAble = false), + (this.textOffsetX = 0), + (this.textOffsetY = 0), + (this.bundleOffset = 20), + (this.bundleGap = 30), + (this.groupNum = 1), + (this.curveness = 0.5), + (this.arrowsRadius = 5), + (this.showlabel = true), + (this.showArrow = true), + (this.arrowType = "arrow"), + (this.labelBackGround = null), + (this.labelBorderWidth = 0), + (this.labelBorderColor = "255,255,255"); + this.path = []; + this.animate = false; + this.animateSpeed = 2; + this.animateBallIndex = 1; + this.animateBall = null; + this.animateBallColor = "255,0,0"; + this.animateBallSize = Math.round(this.lineWidth * 2); + this.lineDashOffset = 0; + }), + (this.findInsertPoint = function (target, source) { + var line = DGraph.util.lineVir( + target.cx, + target.cy, + source.cx, + source.cy, + ), + bound = target.getBound(), + point = DGraph.util.intersectionLineBound(line, bound); + return point; + }), + (this.caculateIndex = function () { + var index = calLineNum(this.source, this.target, false); + if (index > 0) { + this.nodeIndex = index - 1; + } + }), + this.initialize(source, target, label), + (this.removeHandler = function () { + var self = this; + this.source && + this.source.outLinks && + (this.source.outLinks = this.source.outLinks.filter(function (b) { + return b !== self; + })), + this.target && + this.target.inLinks && + (this.target.inLinks = this.target.inLinks.filter(function (b) { + return b !== self; + })); + var lines = findAllLines(this); + lines.forEach(function (line, i) { + line.nodeIndex = i; + }); + }), + (this.getStartPosition = function () { + var cpoint = { x: this.source.cx, y: this.source.cy }; + return cpoint; + }), + (this.getEndPosition = function () { + var epoint = { x: this.target.cx, y: this.target.cy }; + return ( + (epoint = this.findInsertPoint(this.target, this.source)), + null == epoint && + (epoint = { x: this.target.cx, y: this.target.cy }), + epoint + ); + }), + (this.getPath = function () { + var startPoint = this.getStartPosition(), + endPoint = this.getEndPosition(); + return [startPoint, endPoint]; + }), + (this.paintLine = function (ctx, label) {}), + (this.paintArrow = function (ctx, g, h) {}), + (this.paint = function (ctx) {}), + (this.paintText = function (ctx, label) {}), + (this.isInBound = function (mouseX, mouseY) { + var self = this; + if (this.path.length > 0) { + var lineWidth = self.lineWidth; + if (self.labelBackHeight) { + lineWidth = Math.round(self.labelBackHeight / 2); + } + var flag = false; + for (var i = 1; i < this.path.length; i++) { + var startPoint = this.path[i - 1], + endPoint = this.path[i]; + if ( + true == + DGraph.util.containStroke( + startPoint.x, + startPoint.y, + endPoint.x, + endPoint.y, + lineWidth, + mouseX, + mouseY, + ) + ) { + flag = true; + break; + } + } + return flag; + } else { + if (self.bezierPoints) { + var pos = self.bezierPoints; + return DGraph.util.containBerzierStroke( + pos[0], + pos[1], + pos[2], + pos[3], + pos[4], + pos[5], + pos[6], + pos[7], + self.lineWidth, + mouseX, + mouseY, + ); + } + if (self.quadraticPoints) { + var pos = self.quadraticPoints; + return DGraph.util.containQuadraticStroke( + pos[0], + pos[1], + pos[2], + pos[3], + pos[4], + pos[5], + self.lineWidth, + mouseX, + mouseY, + ); + } + } + return false; + }); + } + function Edge(source, target, label) { + (this.initialize = function () { + Edge.prototype.initialize.apply(this, arguments); + }), + this.initialize(source, target, label), + (this.paint = function (ctx, needHideText) { + if (this.drawLine && typeof this.drawLine === "function") { + this.drawLine(ctx); + return false; + } + if (null != this.source && null != this.target) { + if (this.colorType == "source") { + this.strokeColor = this.source.fillColor; + } else if ( + this.colorType == "target" || + this.colorType == "both" + ) { + this.strokeColor = this.target.fillColor; + } + this.groupNum = calLineNum(this.target, this.source, true); + if (this.source === this.target || this.groupNum > 1) { + if (this.source === this.target) { + this.lineType = "curver"; + } else { + this.paintMutilPath(ctx, !needHideText); + return; + } + } + this.paintLine(ctx, label); + } + }); + (this.paintLine = function (ctx, label) { + var lineType = this.lineType; + switch (lineType) { + case "direct": + this.paintDrirectLine(ctx, label); + break; + case "curver": + this.paintCurverLink(ctx, label); + break; + case "vlink": + this.paintVerticalLink(ctx, label); + break; + case "hlink": + this.paintHorizolLink(ctx, label); + break; + case "bezier": + this.paintBezier(ctx, label); + break; + case "vbezier": + this.paintVBezierLink(ctx, label); + break; + case "hbezier": + this.paintHBezierLink(ctx, label); + break; + case "arrowline": + this.paintArrowLine(ctx, label); + break; + case "minderline": + this.paintXMinderLine(ctx, label); + break; + case "gephiline": + this.paintGephiLine(ctx, label); + break; + default: + this.lineType == "direct"; + this.paintDrirectLine(ctx, label); + break; + } + }), + (this.paintAnimateBall = function (ctx) { + var self = this; + var path = self.path; + if (path.length == 2) { + self.animateBallIndex = 1; + } + if ( + self.animateBallIndex > 0 && + self.animateBallIndex < path.length + ) { + var sx = path[self.animateBallIndex - 1].x, + sy = path[self.animateBallIndex - 1].y, + tx = path[self.animateBallIndex].x, + ty = path[self.animateBallIndex].y; + var angle = Math.atan2(ty - sy, tx - sx); + var animateBall = self.animateBall; + if ( + animateBall == null || + animateBall.sx != sx || + animateBall.sy != sy || + animateBall.tx != tx || + animateBall.ty != ty + ) { + var dx = tx - sx, + dy = ty - sy; + var diff = Math.sqrt(dx * dx + dy * dy); + var moves = diff / self.animateSpeed; + self.animateBall = { + x: sx, + y: sy, + sx: sx, + sy: sy, + tx: tx, + ty: ty, + angle: angle, + moves: moves, + }; + } + } else { + self.animateBallIndex = 0; + self.animateBall = null; + } + if (self.animateBall && self.animateBall.moves > 0) { + self.animateBall.moves--; + self.animateBall.x += + self.animateSpeed * Math.cos(self.animateBall.angle); + self.animateBall.y += + self.animateSpeed * Math.sin(self.animateBall.angle); + ctx.save(); + ctx.beginPath(); + ctx.fillStyle = `rgba(${self.animateBallColor},${self.alpha})`; + ctx.arc( + self.animateBall.x, + self.animateBall.y, + self.animateBallSize, + 0, + 2 * Math.PI, + ); + ctx.fill(); + ctx.closePath(); + ctx.restore(); + if (self.animateBall.moves <= 0) { + self.animateBall = null; + self.animateBallIndex++; + } + } else { + self.animateBallIndex++; + } + }), + (this.setLineStyle = function (ctx) { + ctx.lineJoin = "round"; + ctx.lineCap = "round"; + if (this.selected || this.showSelected) { + ctx.strokeStyle = `rgba(${this.selectedColor},${this.selectedAlpha})`; + ctx.lineWidth = this.lineWidth + 2; + } else { + if (this.colorType == "both") { + var grd = ctx.createLinearGradient( + this.source.cx, + this.source.cy, + this.target.cx, + this.target.cy, + ); + if (this.source.fillColor) { + grd.addColorStop( + 0, + `rgba(${this.source.fillColor},${this.alpha || 0.8})`, + ); + } + if (this.target.fillColor) { + grd.addColorStop( + 1, + `rgba(${this.target.fillColor},${this.alpha || 0.8})`, + ); + } + ctx.strokeStyle = grd; + } else { + ctx.strokeStyle = `rgba(${this.strokeColor},${this.alpha || 0.8})`; + } + ctx.lineWidth = this.lineWidth; + } + if (this.lineDash && this.lineDash.length > 1) { + ctx.setLineDash(this.lineDash); + if (this.animate) { + ctx.lineDashOffset = this.lineDashOffset -= 2; + this.lineDashOffset = + this.lineDashOffset < -100 ? 1 : this.lineDashOffset; + } + } + }), + (this.paintMutilPath = function (ctx, showText) { + var start = { x: this.source.cx, y: this.source.cy }, + end = { x: this.target.cx, y: this.target.cy }; + var controlPoints = this.computeControlPoint(start, end); + var lineS = controlPoints.startPoint; + var lineE = controlPoints.endPoint; + var angle = Math.atan2(lineE.y - end.y, lineE.x - end.x); + var insertPoint = end; + var endPoint = end; + if (this.showArrow) { + if (this.target.shape == "rect") { + insertPoint = this.findInsertPoint(this.target, { + cx: lineE.x, + cy: lineE.y, + }); + } else { + var radius = + (this.target.radius + this.target.borderWidth / 2) * + this.target.scaleX; + insertPoint = DGraph.util.pointOnCircle( + end.x, + end.y, + radius, + angle, + ); + } + if (insertPoint == null) { + insertPoint = end; + } + var arrowSize = this.getArrowRadius(); + (endPoint.x = insertPoint.x + arrowSize * Math.cos(angle)), + (endPoint.y = insertPoint.y + arrowSize * Math.sin(angle)); + } + this.path = []; + this.path.push({ x: start.x, y: start.y }), + this.path.push({ x: lineS.x, y: lineS.y }), + this.path.push({ x: lineE.x, y: lineE.y }), + this.path.push({ x: insertPoint.x, y: insertPoint.y }); + this.paintLineBackGround(ctx); + ctx.save(); + ctx.beginPath(); + ctx.moveTo(this.path[0].x, this.path[0].y); + for (var j = 1; j < this.path.length - 1; j++) { + ctx.lineTo(this.path[j].x, this.path[j].y); + } + ctx.lineTo(endPoint.x, endPoint.y); + this.setLineStyle(ctx), ctx.stroke(), ctx.restore(); + if (this.showArrow) { + this.paintSpecialArrow( + ctx, + { x: lineE.x, y: lineE.y }, + insertPoint, + ); + } + if ( + this.animate && + (this.lineDash == null || this.lineDash.length <= 1) + ) { + this.paintAnimateBall(ctx); + } + if (this.showlabel && showText) { + this.paintTextOnLineWithAngle( + ctx, + { cx: lineS.x, cy: lineS.y }, + { cx: lineE.x, cy: lineE.y }, + ); + } + }), + (this.computeControlPoint = function (start, end) { + this.bundleOffset = DGraph.util.getDistance(start, end) * 0.45; + var lineNum = this.groupNum; + var angle = Math.atan2(end.y - start.y, end.x - start.x), + gsPoint = { + x: start.x + this.bundleOffset * Math.cos(angle), + y: start.y + this.bundleOffset * Math.sin(angle), + }, + gtPoint = { + x: end.x + this.bundleOffset * Math.cos(angle - Math.PI), + y: end.y + this.bundleOffset * Math.sin(angle - Math.PI), + }, + bundleGap = (lineNum * this.bundleGap) / 2 - this.bundleGap / 2, + offset = this.bundleGap * this.nodeIndex; + angle -= Math.PI / 2; + var lineS = { + x: + gsPoint.x + + offset * Math.cos(angle) + + bundleGap * Math.cos(angle - Math.PI), + y: + gsPoint.y + + offset * Math.sin(angle) + + bundleGap * Math.sin(angle - Math.PI), + }, + lineE = { + x: + gtPoint.x + + offset * Math.cos(angle) + + bundleGap * Math.cos(angle - Math.PI), + y: + gtPoint.y + + offset * Math.sin(angle) + + bundleGap * Math.sin(angle - Math.PI), + }; + return { startPoint: lineS, endPoint: lineE }; + }), + (this.paintCurverLink = function (ctx) { + var source = this.source, + target = this.target; + var sX = source.cx, + sY = source.cy; + var tX = this.target.cx, + tY = this.target.cy; + var dX = tX - sX, + dY = tY - sY, + sign = sX < tX ? 1 : -1, + cp = {}, + c = {}, + angle = 0, + t = 0.5; + if (source.id === target.id) { + var sSize = (source.radius / 2) * source.scaleX || 20; + this.showArrow = false; + cp = DGraph.util.getSelfLoopControlPoints( + sX, + sY, + sSize + (this.nodeIndex * this.bundleGap) / 2, + ); + c = DGraph.util.getPointOnBezierCurve( + t, + sX, + sY, + tX, + tY, + cp.x1, + cp.y1, + cp.x2, + cp.y2, + ); + angle = Math.atan2(1, 1); + this.path = []; + this.bezierPoints = [sX, sY, cp.x1, cp.y1, cp.x2, cp.y2, tX, tY]; + } else { + cp = DGraph.util.getQuadraticControlPoint( + sX, + sY, + tX, + tY, + 4, + this.curveness, + ); + c = DGraph.util.getPointOnQuadraticCurve( + t, + sX, + sY, + tX, + tY, + cp.x, + cp.y, + ); + angle = Math.atan2(dY * sign, dX * sign); + this.path = []; + this.quadraticPoints = [sX, sY, cp.x, cp.y, tX, tY]; + } + if (this.background) { + ctx.save(); + ctx.beginPath(); + ctx.strokeStyle = `rgba(${this.background},${this.alpha})`; + ctx.lineWidth = this.lineWidth * 3; + ctx.moveTo(sX, sY); + if (source.id === target.id) { + ctx.bezierCurveTo(cp.x1, cp.y1, cp.x2, cp.y2, tX, tY); + } else { + ctx.quadraticCurveTo(cp.x, cp.y, tX, tY); + } + ctx.stroke(); + ctx.restore(); + } + ctx.beginPath(); + ctx.moveTo(sX, sY); + this.setLineStyle(ctx); + if (source.id === target.id) { + ctx.bezierCurveTo(cp.x1, cp.y1, cp.x2, cp.y2, tX, tY); + this.animate = false; + } else { + ctx.quadraticCurveTo(cp.x, cp.y, tX, tY); + } + ctx.stroke(); + if ( + this.animate && + (this.lineDash == null || this.lineDash.length <= 1) + ) { + this.paintCurverAnimate(ctx, sX, sY, tX, tY, cp.x, cp.y); + } + if (this.showArrow) { + var pos1 = DGraph.util.getPointOnQuadraticCurve( + 0.5, + sX, + sY, + tX, + tY, + cp.x, + cp.y, + ); + var pos2 = DGraph.util.getPointOnQuadraticCurve( + 0.52, + sX, + sY, + tX, + tY, + cp.x, + cp.y, + ); + this.paintSpecialArrow( + ctx, + { x: pos1.x, y: pos1.y }, + { x: pos2.x, y: pos2.y }, + ); + } + this.paintLineText(ctx, c.x, c.y, angle); + this.paintLineTips(ctx, { x: c.x, y: c.y }); + }), + (this.paintCurverAnimate = function (ctx, sx, sy, tx, ty, cpx, cpy) { + var self = this; + if (self.animateBall && self.animateBall.moves < 1) { + self.animateBall.moves += 0.02; + var pos = DGraph.util.getPointOnQuadraticCurve( + self.animateBall.moves, + sx, + sy, + tx, + ty, + cpx, + cpy, + ); + ctx.save(); + ctx.beginPath(); + ctx.fillStyle = `rgba(${self.animateBallColor},${self.alpha})`; + ctx.arc(pos.x, pos.y, self.animateBallSize, 0, 2 * Math.PI); + ctx.fill(); + ctx.closePath(); + ctx.restore(); + } else { + self.animateBall = { moves: 0 }; + } + }), + (this.paintArrowLine = function (ctx, b) { + var width = this.lineWidth; + width = width < 4 ? 4 : width; + var pos = { x: this.target.cx, y: this.target.cy }; + if (this.showArrow) { + pos = this.calculateEndPoint(this.source, this.target); + } + var x0 = this.source.cx; + var y0 = this.source.cy; + var x1 = pos.x; + var y1 = pos.y; + if (this.reverse) { + x0 = pos.x; + y0 = pos.y; + x1 = this.source.cx; + y1 = this.source.cy; + } + ctx.save(); + ctx.beginPath(); + if (this.showArrow) { + ctx.arrow(x0, y0, x1, y1, [ + -3 * width, + width, + -4 * width, + 3 * width + 1, + ]); + } else { + ctx.arrow(x0, y0, x1, y1, [0, width]); + } + ctx.lineJoin = "round"; + if (this.colorType == "both") { + var grd = ctx.createLinearGradient( + this.source.cx, + this.source.cy, + this.target.cx, + this.target.cy, + ); + grd.addColorStop( + 0, + `rgba(${this.source.fillColor},${this.alpha})`, + ); + grd.addColorStop( + 1, + `rgba(${this.target.fillColor},${this.alpha})`, + ); + ctx.fillStyle = grd; + } else { + ctx.fillStyle = `rgba(${this.strokeColor},${this.alpha})`; + } + if (this.selected || this.showSelected) { + ctx.fillStyle = `rgba(${this.selectedColor},${this.selectedAlpha})`; + } + ctx.fill(); + ctx.restore(); + this.path = [ + { x: x0, y: y0 }, + { x: x1, y: y1 }, + ]; + this.paintTextOnLineWithAngle(ctx, this.source, this.target); + }), + (this.paintXMinderLine = function (cxt, b) { + var source = this.source, + target = this.target; + var sourceX = source.cx, + sourceY = source.cy; + var targetX = target.cx, + targetY = target.cy; + if (sourceX > targetX) { + targetX = target.x + target.width / 2; + } else if (sourceX < targetX) { + targetX = target.x; + } + cxt.beginPath(); + this.setLineStyle(cxt); + cxt.moveTo(source.cx, source.cy); + cxt.lineTo((sourceX + targetX) / 2, sourceY); + cxt.lineTo((sourceX + targetX) / 2, targetY); + cxt.lineTo(target.cx, target.cy); + cxt.stroke(); + this.path = [ + { x: sourceX, y: sourceY }, + { x: (sourceX + targetX) / 2, y: sourceY }, + { x: (sourceX + targetX) / 2, y: targetY }, + { x: targetX, y: targetY }, + ]; + }), + (this.calculateEndPoint = function (source, target) { + var angle = 0; + this.arrowsRadius = this.getArrowRadius(); + var offset = 0, + posX = target.cx, + posY = target.cy; + if ("rect" == this.target.shape) { + var cod = this.getPath(); + posX = cod[1].x; + posY = cod[1].y; + angle = Math.atan2(posY - cod[0].y, posX - cod[0].x); + } else { + var startPoint = { x: source.cx, y: source.cy }, + endPoint = { x: target.cx, y: target.cy }; + offset = -( + (target.radius + target.borderWidth / 2) * + target.scaleX + ); + (angle = Math.atan2( + endPoint.y - startPoint.y, + endPoint.x - startPoint.x, + )), + (posX = endPoint.x + offset * Math.cos(angle)), + (posY = endPoint.y + offset * Math.sin(angle)); + } + return { x: posX, y: posY, angle: angle }; + }), + (this.paintDrirectLine = function (ctx) { + var posX = this.target.cx, + posY = this.target.cy; + var linetX = posX, + linetY = posY; + if (this.showArrow) { + var endPoint = this.calculateEndPoint(this.source, this.target); + posX = endPoint.x; + posY = endPoint.y; + linetX = posX - this.arrowsRadius * Math.cos(endPoint.angle); + linetY = posY - this.arrowsRadius * Math.sin(endPoint.angle); + } + this.path = [ + { x: this.source.cx, y: this.source.cy }, + { x: this.target.cx, y: this.target.cy }, + ]; + this.paintLineBackGround(ctx); + ctx.beginPath(), ctx.moveTo(this.source.cx, this.source.cy); + this.setLineStyle(ctx); + ctx.lineTo(linetX, linetY), ctx.stroke(); + if (this.showArrow) { + var start, end; + if ("rect" == this.target.shape) { + var cod = this.getPath(); + (start = cod[0]), (end = cod[1]); + } else { + (start = { x: this.source.cx, y: this.source.cy }), + (end = { x: posX, y: posY }); + } + this.paintSpecialArrow(ctx, start, end); + } + if ( + this.animate && + (this.lineDash == null || this.lineDash.length <= 1) + ) { + this.paintAnimateBall(ctx); + } + this.paintTextOnLineWithAngle(ctx, this.source, this.target); + }), + (this.paintTextOnLineWithAngle = function (ctx, start, end) { + if (this.showlabel && this.label) { + let textAngle = Math.atan2(end.cy - start.cy, end.cx - start.cx); + if (textAngle > Math.PI / 2) textAngle = -(Math.PI - textAngle); + if (textAngle < -Math.PI / 2) textAngle = -(-Math.PI - textAngle); + let textPos = { + x: start.cx + (end.cx - start.cx) / 2, + y: start.cy + (end.cy - start.cy) / 2, + }; + this.paintLineText(ctx, textPos.x, textPos.y, textAngle); + } + this.paintLineTips(ctx, { + x: start.cx + (end.cx - start.cx) / 2, + y: start.cy + (end.cy - start.cy) / 2, + }); + }), + (this.paintLineTips = function (ctx, textPos) { + if (this.hideText) { + return false; + } + if (this.tipText) { + var radius = this.tipRadius || 0; + var padding = 12; + ctx.save(); + ctx.font = this.tipFont || "normal 13px Arial"; + var width = ctx.measureText(this.tipText).width + padding; + var height = ctx.measureText("田").width + padding - 2; + radius = radius > height / 2 ? height / 2 : radius; + // radius = height / 2 ; + ctx.translate(textPos.x - width / 2, textPos.y - height - 6); + ctx.beginPath(); + ctx.arc(width - radius, height - radius, radius, 0, Math.PI / 2); + ctx.lineTo(width / 2 + 4, height); + ctx.lineTo(width / 2, height + 6); + ctx.lineTo(width / 2 - 4, height); + ctx.lineTo(radius, height); + ctx.arc(radius, height - radius, radius, Math.PI / 2, Math.PI); + ctx.lineTo(0, radius); + ctx.arc(radius, radius, radius, Math.PI, (Math.PI * 3) / 2); + ctx.lineTo(width - radius, 0); + ctx.arc( + width - radius, + radius, + radius, + (Math.PI * 3) / 2, + Math.PI * 2, + ); + ctx.lineTo(width, height - radius); + ctx.closePath(); + ctx.fillStyle = `rgba(${this.tipColor || "250,50,50"},${this.alpha})`; + ctx.fill(); + ctx.translate(width / 2, height / 2); + ctx.textBaseline = "middle"; + ctx.textAlign = "center"; + ctx.fillStyle = `rgba(${this.tipFontColor || "250,250,250"},${this.alpha})`; + ctx.fillText(this.tipText, 0, 0); + ctx.restore(); + } + }), + (this.getArrowRadius = function () { + var raduis = Math.round(3 * this.lineWidth); + return Math.min(Math.max(raduis, 6), 80); + }), + (this.getTargetBorderPoint = function () { + var radius = 0; + if (this.showArrow) { + var target = this.target; + radius = target.radius; + if ("rect" == target.shape) { + if (["vbezier", "vlink"].indexOf(this.lineType) != -1) { + radius = + (target.height / 2 + target.borderWidth / 2) * + target.scaleX; + } else { + radius = + (target.width / 2 + target.borderWidth / 2) * target.scaleX; + } + } else { + radius = + (target.radius + target.borderWidth / 2) * target.scaleX; + } + } + return radius; + }), + (this.paintTriangleArrow = function (ctx, sourceP, targetP) { + var arrowsRadius = this.getArrowRadius() + 2; + var angle = Math.atan2( + targetP.y - sourceP.y, + targetP.x - sourceP.x, + ); + ctx.save(); + ctx.translate(targetP.x, targetP.y); + ctx.rotate(angle + 1); + if (this.selected || this.showSelected) { + ctx.fillStyle = `rgba(${this.selectedColor},${this.selectedAlpha})`; + } else { + ctx.fillStyle = `rgba(${this.strokeColor},${this.alpha})`; + } + ctx.beginPath(); + ctx.moveTo(0, arrowsRadius); + ctx.lineTo(0, 0); + ctx.rotate(-2); + ctx.lineTo(0, -arrowsRadius); + ctx.closePath(), ctx.fill(), ctx.restore(); + }), + (this.paintSpecialArrow = function (ctx, sourceP, targetP) { + if (this.arrowType == "arrow") { + var arrowsRadius = this.getArrowRadius(); + var angle = Math.atan( + (targetP.x - sourceP.x) / (targetP.y - sourceP.y), + ); + ctx.save(); + ctx.translate(targetP.x, targetP.y); + if (targetP.y - sourceP.y >= 0) { + ctx.rotate(-angle); + } else { + ctx.rotate(Math.PI - angle); + } + if (this.selected || this.showSelected) { + ctx.fillStyle = `rgba(${this.selectedColor},${this.selectedAlpha})`; + } else { + ctx.fillStyle = `rgba(${this.strokeColor},${this.alpha})`; + } + ctx.beginPath(); + ctx.lineTo(-arrowsRadius, -arrowsRadius * 2); + ctx.lineTo(0, -arrowsRadius); + ctx.lineTo(arrowsRadius, -arrowsRadius * 2); + ctx.lineTo(0, 0); + ctx.closePath(); + ctx.fill(); + ctx.restore(); + } else { + this.paintTriangleArrow(ctx, sourceP, targetP); + } + }), + (this.paintVerticalLink = function (cxt) { + var source = this.source, + target = this.target; + var sourceX = source.cx, + sourceY = source.cy; + var targetX = target.cx, + targetY = target.cy; + this.path = [ + { x: sourceX, y: sourceY }, + { x: sourceX, y: (sourceY + targetY) / 2 }, + { x: targetX, y: (sourceY + targetY) / 2 }, + { x: targetX, y: targetY }, + ]; + var radius = 0; + if (this.showArrow) { + radius = this.getTargetBorderPoint(); + if (sourceY > targetY) { + radius = -radius; + } + } + this.paintLineBackGround(cxt); + var lastPath = this.path[this.path.length - 1]; + cxt.beginPath(); + this.setLineStyle(cxt); + cxt.moveTo(this.path[0].x, this.path[0].y); + for (var i = 1; i < this.path.length - 1; i++) { + cxt.lineTo(this.path[i].x, this.path[i].y); + } + if (sourceY >= targetY) { + cxt.lineTo(lastPath.x, targetY - radius + this.getArrowRadius()); + } else { + cxt.lineTo(lastPath.x, targetY - radius - this.getArrowRadius()); + } + cxt.stroke(); + if (this.showArrow) { + this.paintSpecialArrow( + cxt, + { x: targetX, y: (sourceY + targetY) / 2 }, + { x: targetX, y: targetY - radius }, + ); + } + if ( + this.animate && + (this.lineDash == null || this.lineDash.length <= 1) + ) { + this.paintAnimateBall(cxt); + } + this.paintLineText( + cxt, + (sourceX + targetX) / 2, + (targetY + sourceY) / 2, + ); + this.paintLineTips(cxt, { + x: (sourceX + targetX) / 2, + y: (targetY + sourceY) / 2, + }); + }), + (this.paintHorizolLink = function (cxt) { + var source = this.source, + target = this.target; + var sourceX = source.cx, + sourceY = source.cy; + var targetX = target.cx, + targetY = target.cy; + var radius = 0; + if (this.showArrow) { + radius = this.getTargetBorderPoint(); + if (sourceX > targetX) { + radius = -radius; + } + } + this.path = [ + { x: sourceX, y: sourceY }, + { x: (sourceX + targetX - radius) / 2, y: sourceY }, + { x: (sourceX + targetX - radius) / 2, y: targetY }, + { x: targetX, y: targetY }, + ]; + this.paintLineBackGround(cxt); + var lastPath = this.path[this.path.length - 1]; + cxt.beginPath(); + this.setLineStyle(cxt); + cxt.moveTo(this.path[0].x, this.path[0].y); + for (var i = 1; i < this.path.length - 1; i++) { + cxt.lineTo(this.path[i].x, this.path[i].y); + } + if (sourceX >= targetX) { + cxt.lineTo(targetX - radius + this.getArrowRadius(), lastPath.y); + } else { + cxt.lineTo(targetX - radius - this.getArrowRadius(), lastPath.y); + } + cxt.stroke(); + if (this.showArrow) { + this.paintSpecialArrow( + cxt, + { x: (sourceX + targetX) / 2, y: targetY }, + { x: targetX - radius, y: targetY }, + ); + } + if ( + this.animate && + (this.lineDash == null || this.lineDash.length <= 1) + ) { + this.paintAnimateBall(cxt); + } + this.paintLineText( + cxt, + (sourceX + targetX) / 2, + (targetY + sourceY) / 2, + ); + this.paintLineTips(cxt, { + x: (sourceX + targetX) / 2, + y: (targetY + sourceY) / 2, + }); + }), + (this.paintLineBackGround = function (ctx) { + if (this.background) { + ctx.save(); + ctx.beginPath(), (ctx.lineCap = "round"); + ctx.lineJoin = "round"; + ctx.moveTo(this.path[0].x, this.path[0].y); + for (var i = 1; i < this.path.length; i++) { + ctx.lineTo(this.path[i].x, this.path[i].y); + } + ctx.strokeStyle = `rgba(${this.background},${this.alpha})`; + ctx.lineWidth = (this.lineWidth + 2) * 3; + ctx.stroke(), ctx.restore(); + } + }), + (this.paintBezier = function (cxt) { + var angle = DGraph.util.calculateAngle( + this.source.cx, + this.source.cy, + this.target.cx, + this.target.cy, + ); + if ( + (angle >= 0 && angle <= 45) || + (angle >= 135 && angle <= 225) || + (angle >= 315 && angle <= 360) + ) { + this.paintVBezierLink(cxt); + } else { + this.paintHBezierLink(cxt); + } + }), + (this.paintHBezierLink = function (cxt) { + var source = this.source, + target = this.target; + var sourceX = source.cx, + sourceY = source.cy; + var targetX = target.cx, + targetY = target.cy; + var radius = 0, + arrowRadius = this.getArrowRadius(); + if (this.showArrow) { + targetX = + target.cx - + (target.width / 2 + target.borderWidth / 2) * target.scaleX - + arrowRadius; + radius = this.getTargetBorderPoint(); + if (sourceX > target.cx) { + targetX = + target.cx + + (target.width / 2 + target.borderWidth / 2) * target.scaleX + + arrowRadius; + radius = -radius; + } + } + var x3 = (sourceX + targetX) * this.curveness, + y3 = sourceY, + x4 = (sourceX + targetX) * this.curveness, + y4 = targetY; + if (this.background) { + cxt.save(); + cxt.beginPath(); + cxt.strokeStyle = `rgba(${this.background},${this.alpha})`; + cxt.lineWidth = this.lineWidth * 3; + cxt.moveTo(sourceX, sourceY); + cxt.bezierCurveTo(x3, y3, x4, y4, targetX, targetY); + cxt.lineTo(target.cx, target.cy); + cxt.stroke(); + cxt.restore(); + } + cxt.beginPath(); + this.setLineStyle(cxt); + cxt.moveTo(sourceX, sourceY); + cxt.bezierCurveTo(x3, y3, x4, y4, targetX, targetY); + cxt.stroke(); + this.path = []; + this.bezierPoints = [ + sourceX, + sourceY, + x3, + y3, + x4, + y4, + targetX, + targetY, + ]; + if (this.showArrow) { + this.paintSpecialArrow( + cxt, + { x: targetX, y: targetY }, + { x: target.cx - radius, y: target.cy }, + ); + } + if ( + this.animate && + (this.lineDash == null || this.lineDash.length <= 1) + ) { + this.paintBezierAnimate( + cxt, + source.cx, + source.cy, + targetX, + targetY, + x3, + y3, + x4, + y4, + ); + } + this.paintBezierText( + cxt, + source.cx, + source.cy, + targetX, + targetY, + x3, + y3, + x4, + y4, + ); + }), + (this.paintVBezierLink = function (cxt) { + var source = this.source, + target = this.target; + var sourceX = source.cx, + sourceY = source.cy; + var targetX = target.cx, + targetY = target.cy; + var radius = 0; + if (this.showArrow) { + targetY = + target.cy - + (target.height / 2 + target.borderWidth / 2) * target.scaleX - + this.getArrowRadius(); + radius = this.getTargetBorderPoint(); + if (sourceY > target.cy) { + targetY = + target.cy + + (target.height / 2 + target.borderWidth / 2) * target.scaleX + + this.getArrowRadius(); + radius = -radius; + } + } + var x3 = sourceX; + var y3 = (sourceY + targetY) * this.curveness; + var x4 = targetX; + var y4 = (sourceY + targetY) * this.curveness; + if (this.background) { + cxt.save(); + cxt.beginPath(); + cxt.strokeStyle = `rgba(${this.background},${this.alpha})`; + cxt.lineWidth = this.lineWidth * 3; + cxt.moveTo(source.cx, source.cy); + cxt.bezierCurveTo(x3, y3, x4, y4, targetX, targetY); + cxt.lineTo(targetX, target.cy); + cxt.stroke(); + cxt.restore(); + } + cxt.beginPath(); + this.setLineStyle(cxt); + cxt.moveTo(source.cx, source.cy); + cxt.bezierCurveTo(x3, y3, x4, y4, targetX, targetY); + cxt.stroke(); + this.path = []; + this.bezierPoints = [ + source.cx, + source.cy, + x3, + y3, + x4, + y4, + targetX, + targetY, + ]; + if (this.showArrow) { + this.paintSpecialArrow( + cxt, + { x: targetX, y: targetY }, + { x: target.cx, y: target.cy - radius }, + ); + } + this.paintBezierText( + cxt, + source.cx, + source.cy, + targetX, + targetY, + x3, + y3, + x4, + y4, + ); + if ( + this.animate && + (this.lineDash == null || this.lineDash.length <= 1) + ) { + this.paintBezierAnimate( + cxt, + source.cx, + source.cy, + targetX, + targetY, + x3, + y3, + x4, + y4, + ); + } + }), + (this.paintBezierAnimate = function ( + ctx, + sx, + sy, + tx, + ty, + cpx1, + cpy1, + cpx2, + cpy2, + ) { + var self = this; + if (self.animateBall && self.animateBall.moves < 1) { + self.animateBall.moves += 0.02; + var pos = DGraph.util.getPointOnBezierCurve( + self.animateBall.moves, + sx, + sy, + tx, + ty, + cpx1, + cpy1, + cpx2, + cpy2, + ); + ctx.save(); + ctx.beginPath(); + ctx.arc(pos.x, pos.y, self.animateBallSize, 0, 2 * Math.PI); + ctx.closePath(); + ctx.fillStyle = `rgba(${self.animateBallColor},${self.alpha})`; + ctx.fill(); + ctx.restore(); + } else { + self.animateBall = { moves: 0 }; + } + }), + (this.paintBezierText = function ( + cxt, + sx, + sy, + tx, + ty, + cpx1, + cpy1, + cpx2, + cpy2, + ) { + if (this.showlabel && this.label) { + var middle = DGraph.util.getPointOnBezierCurve( + 0.5, + sx, + sy, + tx, + ty, + cpx1, + cpy1, + cpx2, + cpy2, + ); + if (["gephiline", "curver"].indexOf(this.lineType) > -1) { + var sign = cpx2 - cpx1 > 0 ? -1 : 1; + var angle = + Math.atan2((cpx2 - cpx1) * sign, (cpy2 - cpy1) * -sign) + + Math.PI / 2; + this.paintLineText(cxt, middle.x, middle.y, angle); + } else { + this.paintLineText(cxt, middle.x, middle.y); + } + this.paintLineTips(cxt, { x: middle.x, y: middle.y }); + } else { + if (this.tipText) { + var middle = DGraph.util.getPointOnBezierCurve( + 0.5, + sx, + sy, + tx, + ty, + cpx1, + cpy1, + cpx2, + cpy2, + ); + this.paintLineTips(cxt, { x: middle.x, y: middle.y }); + } + } + }), + (this.paintLineText = function (cxt, posX, posY, angle) { + if (!this.showlabel || !this.label || this.hideText) { + return false; + } + var baseLine = "bottom"; + if (this.tipText) { + baseLine = "top"; + } + if (this.labelBackGround) { + var textPadding = 4; + baseLine = "middle"; + cxt.save(); + cxt.font = this.font; + var textWidth = + cxt.measureText(this.label).width + textPadding * 2; + var textHeight = cxt.measureText("田").width + textPadding; + this.labelBackHeight = textHeight; + cxt.translate(posX, posY); + cxt.rotate(angle ? angle : 0); + cxt.lineWidth = 1; + cxt.strokeStyle = `rgba(${this.labelBorderColor},${this.alpha})`; + cxt.fillStyle = `rgba(${this.labelBackGround},${this.alpha})`; + cxt.DGraphRoundRect( + -textWidth / 2, + -textHeight / 2 - 1, + textWidth, + textHeight, + textHeight / 2 - textPadding, + ); + cxt.fill(), cxt.stroke(), cxt.restore(); + } + cxt.save(); + cxt.font = this.font; + cxt.translate(posX, posY); + cxt.rotate(angle ? angle : 0); + cxt.textAlign = "center"; + cxt.textBaseline = baseLine; + cxt.fillStyle = `rgba(${this.fontColor},${this.alpha})`; + cxt.fillText(this.label, 0, 0); + cxt.restore(); + }), + (this.paintGephiLine = function (ctx, label) { + var self = this; + var source = { + x: self.source.cx, + y: self.source.cy, + r: self.width, + }; + var target = { + x: self.target.cx, + y: self.target.cy, + r: self.width, + }; + var arrow_size = self.lineWidth * 3.5; + var x2, y2, x3, y3, x4, y4, x5, y5; + x2 = source.x; + y2 = source.y; + x3 = + 0.3 * target.y - 0.3 * source.y + 0.8 * source.x + 0.2 * target.x; + y3 = + 0.8 * source.y + 0.2 * target.y - 0.3 * target.x + 0.3 * source.x; + x4 = + 0.3 * target.y - 0.3 * source.y + 0.2 * source.x + 0.8 * target.x; + y4 = + 0.2 * source.y + 0.8 * target.y - 0.3 * target.x + 0.3 * source.x; + x5 = target.x; + y5 = target.y; + if (this.background) { + ctx.save(); + ctx.beginPath(); + ctx.strokeStyle = `rgba(${this.background},${this.alpha})`; + ctx.lineWidth = this.lineWidth * 3; + ctx.moveTo(x2, y2); + ctx.bezierCurveTo(x3, y3, x4, y4, x5, y5); + ctx.stroke(); + ctx.restore(); + } + ctx.beginPath(); + ctx.moveTo(x2, y2); + this.setLineStyle(ctx); + ctx.bezierCurveTo(x3, y3, x4, y4, x5, y5); + ctx.stroke(); + this.path = []; + this.bezierPoints = [x2, y2, x3, y3, x4, y4, x5, y5]; + if ( + this.animate && + (this.lineDash == null || this.lineDash.length <= 1) + ) { + this.paintBezierAnimate(ctx, x2, y2, x5, y5, x3, y3, x4, y4); + } + if (self.showArrow) { + var pos1 = DGraph.util.getPointOnBezierCurve( + 0.5, + x2, + y2, + x5, + y5, + x3, + y3, + x4, + y4, + ); + var pos2 = DGraph.util.getPointOnBezierCurve( + 0.52, + x2, + y2, + x5, + y5, + x3, + y3, + x4, + y4, + ); + this.paintSpecialArrow(ctx, pos1, pos2); + } + this.paintBezierText(ctx, x2, y2, x5, y5, x3, y3, x4, y4); + }); + } + (Link.prototype = new DGraph.InteractiveElement()), + (DGraph.Link = Link), + (Edge.prototype = new Link()), + (DGraph.Edge = Edge); + })(DGraph), + (function (DGraph) { + function Group(label, fixed = false) { + (this.initialize = function (label, fixed = false) { + Group.prototype.initialize.apply(this, arguments), + (this.elementType = "group"), + (this.zIndex = DGraph.Group_zIndex), + (this.fixed = fixed), + (this.width = 100), + (this.height = 100), + (this.childs = []), + (this.alpha = 1), + (this.dragable = true), + (this.childDragable = true), + (this.visible = true), + (this.fillColor = "250,250,250"), + (this.borderWidth = 1), + (this.borderColor = "100,100,220"), + (this.selectedBorderWidth = 2), + (this.selectedBorderColor = "30,30,250"), + (this.showHeader = true), + (this.label = label), + (this.textAlign = "center"), + (this.textOffsetX = 6); + (this.font = "normal 14px Arial"), + (this.fontColor = "255,255,255"), + (this.headerColor = "60,60,200"), + (this.headerAlpha = 1), + (this.headerHeight = 36), + (this.padding = 20); + }), + this.initialize(label, fixed), + (this.add = function (nodes) { + var self = this; + if (nodes instanceof Array) { + nodes.forEach((node) => { + self.childs.push(node); + node.parentContainer = this; + }); + } else { + self.childs.push(nodes); + nodes.parentContainer = this; + } + }), + (this.remove = function (node) { + for (var i = 0; i < this.childs.length; i++) { + if (this.childs[i] === node) { + node.dragable = true; + node.parentContainer = null; + this.childs = DGraph.util.removeFromArray( + this.childs, + this.childs[i], + ); + break; + } + } + }), + (this.removeAll = function () { + this.childs.forEach((child) => { + child.dragable = true; + child.parentContainer = null; + }); + this.childs = []; + }), + (this.setLocation = function (x, y) { + var offsetX = x - this.x, + offsetY = y - this.y; + (this.x = x), (this.y = y); + for (var i = 0; i < this.childs.length; i++) { + var node = this.childs[i]; + node.setLocation(node.x + offsetX, node.y + offsetY); + } + }), + (this.paint = function (ctx) { + if (this.visible) { + if (!this.fixed) { + this.ajustSize(); + } + ctx.save(); + ctx.beginPath(); + ctx.rect(this.x, this.y, this.width, this.height); + ctx.closePath(), + (ctx.fillStyle = `rgba(${this.fillColor},${this.alpha})`), + ctx.fill(); + if ( + !this.showSelected && + !this.selected && + this.borderWidth > 0 + ) { + ctx.lineWidth = this.borderWidth; + ctx.strokeStyle = `rgba(${this.borderColor},${this.alpha})`; + ctx.stroke(); + } + if ( + (this.showSelected || this.selected) && + this.selectedBorderWidth > 0 + ) { + ctx.lineWidth = this.selectedBorderWidth; + ctx.strokeStyle = `rgba(${this.selectedBorderColor},${this.alpha})`; + ctx.stroke(); + } + ctx.restore(); + if (this.showHeader) { + this.paintHeader(ctx); + this.paintText(ctx); + } + } + }), + (this.paintHeader = function (ctx) { + ctx.save(); + ctx.beginPath(); + ctx.rect(this.x, this.y, this.width, this.headerHeight); + ctx.closePath(); + (ctx.fillStyle = `rgba(${this.headerColor},${this.headerAlpha})`), + ctx.fill(); + var lineWidth = this.borderWidth; + if (this.showSelected || this.selected) { + lineWidth = this.selectedBorderWidth; + } + if (lineWidth > 0) { + ctx.lineWidth = lineWidth; + (ctx.strokeStyle = `rgba(${this.headerColor},${this.headerAlpha})`), + ctx.stroke(); + } + ctx.restore(); + }), + (this.paintText = function (ctx) { + if (this.label) { + ctx.save(); + ctx.font = this.font; + var textWidth = ctx.measureText(this.label).width; + var pos = this.getTextPostion(textWidth); + ctx.textBaseline = "middle"; + ctx.fillStyle = `rgba(${this.fontColor},${this.headerAlpha})`; + ctx.fillText(this.label, pos.x, pos.y); + ctx.restore(); + } + }), + (this.getTextPostion = function (textWidth) { + var position = null; + switch (this.textAlign) { + case "center": + position = { + x: this.x + this.width / 2 - textWidth / 2, + y: this.y + this.headerHeight / 2, + }; + break; + case "right": + position = { + x: this.x + this.width - textWidth - this.textOffsetX, + y: this.y + this.headerHeight / 2, + }; + break; + default: + position = { + x: this.x + this.textOffsetX, + y: this.y + this.headerHeight / 2, + }; + break; + } + return position; + }), + (this.paintMouseover = function (ctx) { + this.showSelected = true; + }), + (this.isInBound = function (x, y) { + return ( + x > this.x && + x < this.x + this.width && + y > this.y && + y < this.y + this.height + ); + }), + (this.ajustSize = function () { + var self = this; + var nodeCount = self.childs.length; + if (nodeCount > 0) { + var left = 1e7, + right = -1e7, + top = 1e7, + bottom = -1e7, + width = right - left, + height = bottom - top; + for (var i = 0; i < nodeCount; i++) { + var node = self.childs[i]; + node.dragable = self.childDragable; + var scaleX = node.scaleX == 1 ? 0 : node.scaleX; + var scaleY = node.scaleY == 1 ? 0 : node.scaleY; + node.x <= left && (left = node.x - (node.width * scaleX) / 2), + node.x >= right && (right = node.x), + node.y <= top && (top = node.y - (node.height * scaleY) / 2), + node.y >= bottom && (bottom = node.y), + (width = right - left + node.width * node.scaleX), + (height = bottom - top + node.height * node.scaleY); + } + self.x = left - self.padding; + self.y = top - self.padding - self.headerHeight; + self.width = Math.max(width, 60) + self.padding * 2; + self.height = + Math.max(height, 60) + self.padding * 2 + self.headerHeight; + } + }); + } + (Group.prototype = new DGraph.InteractiveElement()), + (DGraph.Group = Group); + })(DGraph), + (function (DGraph) { + function ColorUtils() { + function hexToRgb(hex) { + var h = hex.replace("#", ""); + return [ + parseInt(h.substring(0, 2), 16), + parseInt(h.substring(2, 4), 16), + parseInt(h.substring(4, 6), 16), + ]; + } + function hexToRgbStr(hex) { + var rgb = hexToRgb(hex); + return `${rgb[0]},${rgb[1]},${rgb[2]}`; + } + function hex(c) { + var s = "0123456789abcdef"; + var i = parseInt(c, 10); + if (i === 0 || isNaN(c)) { + return "00"; + } + i = Math.round(Math.min(Math.max(0, i), 255)); + return s.charAt((i - (i % 16)) / 16) + s.charAt(i % 16); + } + function convertRgbToHex(rgb) { + var aColor = rgb + .replace(/(?:(|)|rgb|RGB)*/g, "") + .replace("(", "") + .replace(")", "") + .split(","); + return "#" + hex(aColor[0]) + hex(aColor[1]) + hex(aColor[2]); + } + function generateGradientColor(colorStart, colorEnd, colorCount) { + var start = hexToRgb(colorStart); + var end = hexToRgb(colorEnd); + var alpha = 0.0; + var rt = []; + for (var i = 0; i < colorCount; i++) { + var c = []; + alpha += 1.0 / colorCount; + c[0] = start[0] * alpha + (1 - alpha) * end[0]; + c[1] = start[1] * alpha + (1 - alpha) * end[1]; + c[2] = start[2] * alpha + (1 - alpha) * end[2]; + rt.push(`${c[0]},${c[1]},${c[2]}`); + } + return rt; + } + function genRgbGradientColors(numSquares, firstRgb, secondRgb) { + var rgb = []; + var first = firstRgb.split(","); + var second = secondRgb.split(","); + var diff1 = first[0] - second[0]; + var diff2 = first[1] - second[1]; + var diff3 = first[2] - second[2]; + var mult1 = diff1 / (numSquares - 1); + var mult2 = diff2 / (numSquares - 1); + var mult3 = diff3 / (numSquares - 1); + var colour = new Array(3); + for (var i = 0; i < numSquares; i++) { + colour[0] = Math.floor(first[0] - mult1 * i); + colour[1] = Math.floor(first[1] - mult2 * i); + colour[2] = Math.floor(first[2] - mult3 * i); + rgb.push(`${colour[0]},${colour[1]},${colour[2]}`); + } + return rgb; + } + return { + generateGradientColor: generateGradientColor, + genRgbGradientColors: genRgbGradientColors, + colorHex: hexToRgbStr, + convertRgbToHex: convertRgbToHex, + }; + } + DGraph.ColorUtils = ColorUtils; + })(DGraph); + var VisualGraph = function (container, config) { + if (container == null) { + return; + } + this.defaultConfig = { + node: { + showlabel:true, + label: { + show: false, + color: "50,50,50", + font: "normal 1px Arial", + wrapText: true, + textPosition: "Bottom_Center", + textOffsetX: 0, + textOffsetY: 0, + background: null, + borderWidth: 0, + borderColor: null, + }, + shape: "circle", + color: "30,160,255", + borderColor: "20,20,20", + borderAlpha: 1, + borderWidth: 0, + borderRadius: 0, + lineDash: [0], + alpha: 1, + size: 0, + width: 60, + height: 60, + image: null, + selected: { + borderColor: "140,248,241", + borderAlpha: 1, + borderWidth: 10, + showShadow: true, + shadowColor: "140,248,241", + shadowBlur: 100, + }, + onClick: function (event, node) {}, + ondblClick: function (event, node) {}, + onMouseUp: function (event, node) {}, + onMouseDown: function (event, node) {}, + onMouseOver: function (event, node) {}, + onMouseOut: function (event, node) {}, + onMousedrag: function (event, node) {}, + }, + link: { + label: { show: true, color: "20,20,20", font: "normal 13px Arial" }, + lineType: "direct", + colorType: "defined", + color: "10,10,10", + alpha: 1, + lineWidth: 2, + lineDash: [0], + showArrow: true, + arrowType: "arrow", + selected: { + color: "255,0,0", + alpha: 1, + lineWidth: 8, + showShadow: false, + shadowColor: "10,250,10", + }, + onClick: function (event, link) {}, + ondblClick: function (event, link) {}, + onMouseUp: function (event, link) {}, + onMouseDown: function (event, link) {}, + onMouseOver: function (event, link) {}, + onMouseOut: function (event, link) {}, + }, + highLightNeiber: false, + wheelZoom: 1, + noElementClick: function (event, graphvis) {}, + noElementkeyUp: function (event) {}, + noElementkeyDown: function (event) {}, + mouseWheel: function (event) {}, + mouseDrag: function (event) {}, + onBoxSelectEndEvent: function (selectedNodes) {}, + }; + var self = this; + this.autoLayout = false; + this.forceOptions = { + size: [1200, 800], + friction: 0.75, + linkDistance: 120, + linkStrength: 0.05, + charge: -200, + gravity: 0.0001, + noverlap: false, + alpha: 0.8, + theta: 0.5, + loopName: null, + }; + if (!config) { + this.config = this.defaultConfig; + } else { + this.resetConfig(config); + } + var miniMapConfig = this.deepExtend( + { container: "default", width: 200, height: 160, viewColor: "#3ca9f1" }, + this.config.miniMap || {}, + true, + true, + ); + this.stage = new DGraph.Stage(container, miniMapConfig); + this.canvas = this.stage.canvas; + this.scene = null; + this.nodes = []; + this.links = []; + this.nodeIdIndex = 1; + this.currentNode = null; + this.currentLink = null; + this.showLinkFlag = true; + this.isDerictedGraph = true; + this.clusterGroups = []; + this.currentCluster = null; + this.currentLayout = null; + this.drawLinkFlag = false; + this.virNode = null; + this.drawLineCallback = function (link) { + return link; + }; + this.sceneEvent = null; + this.init(); + }; + VisualGraph.prototype.init = function () { + var self = this; + this.stage.wheelZoom = this.config.wheelZoom; + if (self.scene != null) { + self.scene.clear(); + self.stage.remove(self.scene); + } + self.scene = new DGraph.Scene(this.stage); + self.nodes = []; + self.links = []; + self.initDrawLinkBase(); + }; + VisualGraph.prototype.resetConfig = function (config) { + var self = this; + config.node.showlabel = true; + this.config = this.deepExtend(this.defaultConfig, config || {}, true, true); + if ( + this.config.layout && + this.config.layout["type"] == "force" && + this.config.layout["options"] + ) { + this.autoLayout = true; + setTimeout(function () { + (self.forceOptions.friction = + Number(self.config.layout.options["friction"]) || 0.9), + (self.forceOptions.linkDistance = + Number(self.config.layout.options["linkDistance"]) || 150), + (self.forceOptions.linkStrength = + Number(self.config.layout.options["linkStrength"]) || 0.05), + (self.forceOptions.charge = + Number(self.config.layout.options["charge"]) || -150), + (self.forceOptions.gravity = + Number(self.config.layout.options["gravity"]) || 0.01), + (self.forceOptions.noverlap = + self.config.layout.options["noverlap"] || false); + self.forceOptions.size = [ + self.stage.width || 1200, + self.stage.height || 600, + ]; + self.autoLayout = true; + }, 1000); + } + this.config.showlabel = true; + this.defaultNodeColor = this.config.node.color; + this.highLightNeiber = this.config.highLightNeiber; + }; + VisualGraph.prototype.initDrawLinkBase = function () { + var _self = this; + var virNode = new DGraph.Node(); + virNode.shape = "circle"; + virNode.radius = 1; + virNode.width = 2; + virNode.height = 2; + virNode.show = virNode; + virNode.alpha = 0; + _self.virNode = virNode; + _self.scene.mousemove(function (e) { + if (_self.drawLinkFlag) { + if (!_self.virNode.show) { + _self.virNode.show = true; + _self.scene.add(_self.virNode); + } + if (_self.virLink == null) { + var linkConfig = _self.config["link"]; + var virLink = new DGraph.Edge(_self.currentNode, _self.virNode); + virLink.lineWidth = linkConfig["lineWidth"]; + virLink.showArrow = linkConfig["showArrow"]; + virLink.strokeColor = linkConfig["color"]; + virLink.selectedColor = linkConfig["color"]; + virLink.lineDash = [3, 5, 5]; + _self.virLink = virLink; + _self.scene.add(_self.virLink); + } + _self.virNode.cx = e.x - 2; + _self.virNode.cy = e.y + 2; + } + }); + _self.scene.dbclick(function (e) { + _self.drawLinkFlag = false; + if (_self.virNode.show) { + _self.virNode.show = false; + _self.scene.remove(_self.virNode); + } + if (_self.virLink) { + _self.scene.remove(_self.virLink); + _self.virLink = null; + } + }); + _self.scene.click(function (e) { + _self.hideAllRightMenu(); + if (e.target == null) { + if (_self.highLightNeiber) { + _self.restoreHightLight(); + } + } + }); + _self.scene.mouseup(function (e) { + _self.setMouseModel("normal"); + var mouseUp = _self.config.mouseUp; + if (mouseUp && typeof mouseUp === "function") { + mouseUp(e, _self); + } + }); + _self.scene.keyup(function (e) { + let noElementkeyUp = _self.config.noElementkeyUp; + if (noElementkeyUp && typeof noElementkeyUp === "function") { + noElementkeyUp(e, _self); + } + }); + _self.scene.keydown(function (e) { + let noElementkeyDown = _self.config.noElementkeyDown; + if (noElementkeyDown && typeof noElementkeyDown === "function") { + if (e.key === "Control") { + _self.setMouseModel("select"); + } + noElementkeyDown(e, _self); + } + }); + _self.scene.mousedrag(function (e) { + var mouseDrag = _self.config.mouseDrag; + if (mouseDrag && typeof mouseDrag === "function") { + mouseDrag(e, _self); + } + }); + _self.scene.mouseup(function (e) { + if (e.target == null) { + _self.hideAllRightMenu(); + _self.currentNode = null; + _self.currentLink = null; + if (_self.highLightNeiber) { + _self.restoreHightLight(); + } + var noElementClick = _self.config["noElementClick"]; + if (noElementClick && typeof noElementClick === "function") { + noElementClick(e, _self); + } + } else { + if (e.target.elementType == "node") { + _self.currentLink = null; + } else { + _self.currentNode = null; + } + } + if (this.areaSelect) { + var onBoxSelectEndEvent = _self.config["onBoxSelectEndEvent"]; + if (onBoxSelectEndEvent && typeof onBoxSelectEndEvent === "function") { + var $ = this; + setTimeout(function () { + onBoxSelectEndEvent($.boxSelectedNodes); + $.boxSelectedNodes = null; + }, 500); + } else { + this.boxSelectedNodes = null; + } + } + }); + }; + VisualGraph.prototype.deepExtend = function ( + a, + b, + protoExtend, + allowDeletion, + ) { + for (var prop in b) { + if (b.hasOwnProperty(prop) || protoExtend === true) { + if (b[prop] && b[prop].constructor === Object) { + if (a[prop] === undefined) { + a[prop] = {}; + } + if (a[prop].constructor === Object) { + this.deepExtend(a[prop], b[prop], protoExtend); + } else { + if ( + b[prop] === null && + a[prop] !== undefined && + allowDeletion === true + ) { + delete a[prop]; + } else { + a[prop] = b[prop]; + } + } + } else if (Array.isArray(b[prop])) { + a[prop] = []; + for (let i = 0; i < b[prop].length; i++) { + a[prop].push(b[prop][i]); + } + } else { + if ( + b[prop] === null && + a[prop] !== undefined && + allowDeletion === true + ) { + delete a[prop]; + } else { + a[prop] = b[prop]; + } + } + } + } + return a; + }; + VisualGraph.prototype.hideAllRightMenu = function () { + var _self = this; + if (_self.config.hasOwnProperty("rightMenu")) { + if (_self.config.rightMenu.hasOwnProperty("nodeMenu")) { + var nodeHide = _self.config["rightMenu"]["nodeMenu"]["hide"]; + if (typeof nodeHide === "function") { + _self.config["rightMenu"]["nodeMenu"].hide(); + } + } + if (_self.config.rightMenu.hasOwnProperty("linkMenu")) { + var linkHide = _self.config["rightMenu"]["linkMenu"]["hide"]; + if (typeof linkHide === "function") { + _self.config["rightMenu"]["linkMenu"].hide(); + } + } + if (_self.config.rightMenu.hasOwnProperty("clusterMenu")) { + var clusterHide = _self.config["rightMenu"]["clusterMenu"]["hide"]; + if (typeof clusterHide === "function") { + _self.config["rightMenu"]["clusterMenu"].hide(); + } + } + } + }; + VisualGraph.prototype.drawData = function (data) { + var self = this; + if (data == null) { + return; + } + this.init(); + var nodeIdMapNode = {}; + var node = null; + (data.nodes || []).forEach(function (n, i) { + n.scale = 1; + node = self.newNode(n, i); + node.showlabel = true; + self.scene.add(node); + self.nodes.push(node); + nodeIdMapNode[n.id] = node; + }); + var source, target, link; + (data.links || []).forEach(function (l) { + source = nodeIdMapNode[l.source]; + target = nodeIdMapNode[l.target]; + if (source && target) { + link = self.newEdge(source, target); + link.showlabel = l.showlabel || self.config.link.label.show; + link.fontColor = l.fontColor || self.config.link.label.color; + link.font = l.font || self.config.link.label.font; + link.colorType = l.colorType || self.config.link.colorType; + link.strokeColor = self.getColor(l, self.config.link.color); + link.lineType = l.lineType || self.config.link.lineType; + link.type = l.type || ""; + link.id = l.id || ""; + link.label = l.label || l.type || ""; + link.weight = l.weight || 2; + link.value = l.value || 2; + link.lineWidth = l.lineWidth || self.config.link.lineWidth; + link.properties = l.properties || {}; + self.scene.add(link); + self.links.push(link); + } + }); + self.refresh(); + if (self.autoLayout) { + self.runLayoutEngin(self.nodes, self.links, 0.9); + } + }; + VisualGraph.prototype.customStyle = function (callback) { + if (callback && typeof callback === "function") { + return callback(this.stage.graphics); + } + return null; + }; + VisualGraph.prototype.translateToCenter = function () { + this.scene.translateToCenter(); + this.refresh(); + }; + VisualGraph.prototype.getColor = function (ele, defaultColor) { + var color = ele.color; + if (color) { + color = color.replace("rgb(", "").replace(")", ""); + } else { + color = defaultColor || this.defaultNodeColor; + } + return color; + }; + VisualGraph.prototype.addNodesInGroup = function (conf, childNodes) { + conf = conf || {}; + var group = new DGraph.Group(conf.label, conf.fixed || false); + group.x = conf.x || 500; + group.y = conf.y || 300; + group.fillColor = conf.color || "150,250,200"; + group.alpha = conf.alpha || 0.8; + group.width = conf.width || 100; + group.height = conf.height || 100; + this.scene.add(group); + if (Array.isArray(childNodes)) { + childNodes.forEach((node) => { + group.add(node); + }); + } else { + group.add(childNodes); + } + this.refresh(); + return group; + }; + VisualGraph.prototype.createGroup = function (conf) { + conf = conf || {}; + var group = new DGraph.Group(conf.label, conf.fixed || false); + group.x = conf.x || 10; + group.y = conf.y || 10; + group.fillColor = conf.color || "150,250,200"; + group.alpha = conf.alpha || 0.8; + group.width = conf.width || 100; + group.height = conf.height || 100; + this.scene.add(group); + this.refresh(); + return group; + }; + VisualGraph.prototype.newNode = function (n) { + var self = this; + var node = new DGraph.Node(); + node.x = n.x == null ? Math.round(Math.random() * this.stage.width) : n.x; + node.y = n.y == null ? Math.round(Math.random() * this.stage.height) : n.y; + node.id = n.id; + node.type = n.type || "default"; + node.cluster = n.cluster || "default"; + node.scaleX = node.scaleY = n.scale || Number(1); + node.label = n.label || n.id; + node.alpha = n.alpha || self.config.node.alpha; + node.fontColor = n.fontColor || self.config.node.label.color; + node.textPosition = n.textPosition || self.config.node.label.textPosition; + node.font = n.font || self.config.node.label.font; + node.borderWidth = n.borderWidth || self.config.node.borderWidth; + node.borderRadius = n.borderRadius || self.config.node.borderRadius; + node.borderColor = n.borderColor || self.config.node.borderColor; + node.borderAlpha = n.borderAlpha || self.config.node.borderAlpha; + node.showlabel = n.showlabel || self.config.node.label.show; + node.wrapText = self.config.node.label.wrapText; + node.showShadow = n.showShadow || self.config.node.selected.showShadow; + node.shadowColor = n.shadowColor || self.config.node.selected.shadowColor; + node.shadowBlur = n.shadowBlur || self.config.node.selected.shadowBlur; + node.selectedBorderColor = + n.selectedBorderColor || self.config.node.selected.borderColor; + node.selectedBorderAlpha = + n.selectedBorderAlpha || self.config.node.selected.borderAlpha; + node.selectedBorderWidth = + n.selectedBorderWidth || self.config.node.selected.borderWidth; + node.lineDash = n.lineDash || self.config.node.lineDash; + node.labelBackGround = + n.labelBackGround || self.config.node.label.background; + node.labelBorderWidth = + n.labelBorderWidth || self.config.node.label.borderWidth; + node.labelBorderColor = + n.labelBorderColor || self.config.node.label.borderColor; + node.textOffsetX = n.textOffsetX || self.config.node.label.textOffsetX; + node.textOffsetY = n.textOffsetY || self.config.node.label.textOffsetY; + node.icon = n.icon; + node.tipText = n.tipText; + node.weight = 1; + node.px = node.x; + node.py = node.y; + node.charge = self.forceOptions.charge; + node.properties = n.properties || {}; + if (self.config.clusters && self.config.clusters[node.cluster]) { + node.fillColor = + self.config.clusters[node.cluster]["color"] || self.config.node.color; + node.shape = + self.config.clusters[node.cluster]["shape"] || self.config.node.shape; + node.size = + self.config.clusters[node.cluster]["size"] || self.config.node.size; + } else { + node.fillColor = self.getColor(n, this.defaultNodeColor); + node.shape = n.shape || self.config.node.shape; + node.size = n.size || self.config.node.size; + } + if (!node.size || node.size == 0) { + node.width = n.width || self.config.node.width; + node.height = n.height || self.config.node.height; + } else { + node.width = node.height = node.size; + } + if (n.image && n.image.length > 0) { + node.setImage(n.image); + } else { + node.setImage(self.config.node.image || ""); + } + node.click(function (event) { + self.currentNode = this; + if (self.drawLinkFlag && self.virLink != null) { + var link = self.newEdge(self.virLink.source, this); + link.strokeColor = self.config.link.color; + self.links.push(link); + self.scene.add(link); + if (self.virNode.show) { + self.virNode.show = false; + self.scene.remove(self.virNode); + } + self.drawLinkFlag = false; + if (self.virLink) { + self.scene.remove(self.virLink); + self.virLink = null; + } + if ( + self.drawLineCallback && + typeof self.drawLineCallback === "function" + ) { + self.drawLineCallback(link); + } + } + if (self.highLightNeiber) { + self.restoreHightLight(); + self.highLightNeiberNodes(this, 0.1); + } + if (self.config.node.hasOwnProperty("onClick")) { + var onClick = self.config.node["onClick"]; + if (typeof onClick === "function") { + onClick(event, this); + } + } + }); + node.dbclick(function (evt) { + this.fixed = !this.fixed; + if (self.config.node.hasOwnProperty("ondblClick")) { + var ondblClick = self.config.node["ondblClick"]; + if (typeof ondblClick === "function") { + ondblClick(event, this); + } + } + }); + node.mousedrag(function (evt) { + this.fixed = true; + this.px = this.x; + this.py = this.y; + if (this.isDragging || self.scene.selectedElements.length > 1) { + return false; + } + this.isDragging = true; + if (self.autoLayout) { + self.runLayoutEngin(self.nodes, self.links, 0.5); + } + if (self.config.node.hasOwnProperty("onMousedrag")) { + var onMousedrag = self.config.node["onMousedrag"]; + if (typeof onMousedrag === "function") { + onMousedrag(event, this); + } + } + }); + node.mouseup(function (evt) { + self.currentNode = this; + this.fixed = true; + this.px = this.x; + this.py = this.y; + this.isDragging = false; + if (evt.button == 2) { + self.showNodeRightMenu(evt, this); + } + if (self.config.node.hasOwnProperty("onMouseUp")) { + var onMouseUp = self.config.node["onMouseUp"]; + if (typeof onMouseUp === "function") { + onMouseUp(event, this); + } + } + }); + node.mousedown(function (evt) { + self.currentNode = this; + this.fixed = true; + this.px = this.x; + this.py = this.y; + if (self.config.node.hasOwnProperty("onMouseDown")) { + var onMouseDown = self.config.node["onMouseDown"]; + if (typeof onMouseDown === "function") { + onMouseDown(event, this); + } + } + }); + node.mouseover(function (evt) { + if (self.config.node.hasOwnProperty("onMouseOver")) { + var onMouseOver = self.config.node["onMouseOver"]; + if (typeof onMouseOver === "function") { + onMouseOver(event, this); + } + } + }); + node.mouseout(function (evt) { + if (self.config.node.hasOwnProperty("onMouseOut")) { + var onMouseOut = self.config.node["onMouseOut"]; + if (typeof onMouseOut === "function") { + onMouseOut(event, this); + } + } + }); + return node; + }; + VisualGraph.prototype.refresh = function () { + this.stage && + ((this.stage.needRepaint = true), + this.stage.repaint(), + (this.stage.needRepaint = false)); + }; + VisualGraph.prototype.highLightNeiberNodes = function (_node, alpha) { + var self = this; + self.nodes.map(function (n) { + n.t_alpha = n.alpha; + n.alpha = alpha || 0.1; + }); + _node.alpha = _node.t_alpha; + (_node.inLinks || []).map(function (link) { + if (link.source.visible) { + link.source.alpha = link.source.t_alpha; + } + }); + (_node.outLinks || []).map(function (link) { + if (link.target.visible) { + link.target.alpha = link.target.t_alpha; + } + }); + if (self.showLinkFlag) { + self.links.map(function (link) { + link.visible = false; + }); + (_node.inLinks || []).map(function (link) { + if (link.source.visible) { + link.visible = true; + } + }); + (_node.outLinks || []).map(function (link) { + if (link.target.visible) { + link.visible = true; + } + }); + } + }; + VisualGraph.prototype.restoreHightLight = function () { + var self = this; + self.nodes.map(function (n) { + n.alpha = n.t_alpha || n.alpha; + }); + if (self.showLinkFlag) { + self.links.map(function (link) { + if (link.source.visible && link.target.visible) { + link.visible = true; + } + }); + } + }; + VisualGraph.prototype.lockNode = function (node) { + if (node && node != null) { + node.fixed = true; + node.dragable = false; + } + }; + VisualGraph.prototype.unLockNode = function (node) { + if (node && node != null) { + node.fixed = false; + node.dragable = true; + } + }; + VisualGraph.prototype.showNodeRightMenu = function (event, _node) { + var self = this; + if (self.config.hasOwnProperty("rightMenu")) { + if (self.config["rightMenu"].hasOwnProperty("nodeMenu")) { + var nodeShow = self.config["rightMenu"]["nodeMenu"]["show"]; + if (typeof nodeShow === "function") { + self.config["rightMenu"]["nodeMenu"].show(event, self, _node); + } + } + } + }; + VisualGraph.prototype.showLinkRightMenu = function (event, link) { + var self = this; + if (self.config.hasOwnProperty("rightMenu")) { + if (self.config["rightMenu"].hasOwnProperty("linkMenu")) { + var linkShow = self.config["rightMenu"]["linkMenu"]["show"]; + if (typeof linkShow === "function") { + self.config["rightMenu"]["linkMenu"].show(event, self, link); + } + } + } + }; + VisualGraph.prototype.showClusterRightMenu = function (event, group) { + var self = this; + if (self.config.hasOwnProperty("rightMenu")) { + if (self.config["rightMenu"].hasOwnProperty("groupMenu")) { + var show = self.config["rightMenu"]["groupMenu"]["show"]; + if (typeof show === "function") { + self.config["rightMenu"]["groupMenu"].show(event, self, group); + } + } + } + }; + VisualGraph.prototype.colorHex = function (hexColor) { + var ctool = new DGraph.ColorUtils(); + return ctool.colorHex(hexColor) || "0,0,250"; + }; + VisualGraph.prototype.newEdge = function (source, target, visible = true) { + if (source == null || target == null) { + return false; + } + var self = this; + var link = new DGraph.Edge(source, target, null); + link.lineWidth = self.config.link.lineWidth; + link.alpha = self.config.link.alpha; + link.strokeColor = self.config.link.color; + link.showArrow = self.config.link.showArrow; + link.arrowType = self.config.link.arrowType; + link.lineType = self.config.link.lineType; + link.lineDash = self.config.link.lineDash; + link.fontColor = self.config.link.label.color; + link.showShadow = self.config.link.selected.showShadow; + link.shadowColor = self.config.link.selected.shadowColor; + link.selectedColor = self.config.link.selected.color; + link.selectedAlpha = self.config.link.selected.alpha; + link.colorType = self.config.link.colorType; + link.labelBackGround = self.config.link.label.background; + link.labelBorderWidth = self.config.link.label.borderWidth; + link.labelBorderColor = self.config.link.label.borderColor; + link.distance = self.forceOptions.linkDistance; + link.strength = self.forceOptions.linkStrength; + link.weight = 1; + link.visible = visible; + ++source.weight; + ++target.weight; + link.mouseup(function (evt) { + self.currentLink = this; + if (evt.button == 2) { + self.showLinkRightMenu(evt, this); + } + }); + link.click(function (event) { + if (self.config.link.hasOwnProperty("onClick")) { + var onClick = self.config.link["onClick"]; + if (typeof onClick === "function") { + onClick(event, this); + } + } + }); + link.dbclick(function (event) { + if (self.config.link.hasOwnProperty("ondblClick")) { + var ondbClick = self.config.link["ondblClick"]; + if (typeof ondbClick === "function") { + ondbClick(event, this); + } + } + }); + link.mousedown(function (event) { + if (self.config.link.hasOwnProperty("onMouseDown")) { + var onMouseDown = self.config.link.onMouseDown; + if (typeof onMouseDown === "function") { + onMouseDown(event, this); + } + } + }); + link.mouseup(function (event) { + if (self.config.link.hasOwnProperty("onMouseUp")) { + var onMouseUp = self.config.link.onMouseUp; + if (typeof onMouseUp === "function") { + onMouseUp(event, this); + } + } + }); + link.mouseover(function (event) { + if (self.config.link.hasOwnProperty("onMouseOver")) { + var onMouseOver = self.config.link["onMouseOver"]; + if (typeof onMouseOver === "function") { + onMouseOver(event, this); + } + } + }); + link.mouseout(function (event) { + if (self.config.link.hasOwnProperty("onMouseOut")) { + var onMouseOut = self.config.link["onMouseOut"]; + if (typeof onMouseOut === "function") { + onMouseOut(event, this); + } + } + }); + return link; + }; + VisualGraph.prototype.addNodes = function (nodes) { + if ((!nodes) instanceof Array || nodes.length == 0) { + return; + } + var self = this; + var nodeIdMapNode = new Map(); + self.nodes.forEach((node) => { + nodeIdMapNode.set(node.id, node); + }); + var newNodes = [], + node = null; + nodes.forEach((_node) => { + if (nodeIdMapNode.get(_node.id) == null) { + node = self.newNode(_node); + self.scene.add(node); + self.nodes.push(node); + newNodes.push(node); + } + }); + self.refresh(); + return newNodes; + }; + VisualGraph.prototype.addNode = function (_node) { + var self = this; + var node = self.newNode(_node); + self.scene.add(node); + self.nodes.push(node); + self.refresh(); + return node; + }; + VisualGraph.prototype.addEdges = function (edges) { + if ((!edges) instanceof Array || edges.length == 0) { + return; + } + var self = this; + var linkIdMapLink = new Map(); + self.links.forEach((link) => { + if (link.id) { + linkIdMapLink.set(link.id, link); + } + }); + var idMapNode = new Map(); + self.nodes.forEach((node) => { + idMapNode.set(node.id, node); + }); + var newEdges = []; + var sourceNode = null, + targetNode = null, + link = null; + edges.forEach((_link) => { + if (linkIdMapLink.get(_link.id) == null) { + sourceNode = idMapNode.get(_link.source); + targetNode = idMapNode.get(_link.target); + if (sourceNode != null && targetNode != null) { + link = self.newEdge(sourceNode, targetNode); + self._copyEdgePropers(link, _link); + self.scene.add(link); + self.links.push(link); + newEdges.push(link); + } + } + }); + self.refresh(); + return newEdges; + }; + VisualGraph.prototype.addEdge = function (_link) { + var self = this; + var sourceNode = this.nodes.filter(function (n) { + return n.id == _link.source; + })[0]; + var targetNode = this.nodes.filter(function (n) { + return n.id == _link.target; + })[0]; + var link = null; + if (sourceNode && targetNode) { + link = self.newEdge(sourceNode, targetNode); + self._copyEdgePropers(link, _link); + self.scene.add(link); + self.links.push(link); + self.refresh(); + } + return link; + }; + VisualGraph.prototype._copyEdgePropers = function (link, edge) { + if (link != null && edge != null) { + var self = this; + link.id = edge.id || ""; + link.type = edge.type || "default"; + link.label = edge.label || ""; + link.showlabel = edge.showlabel || self.config.link.label.show; + link.lineWidth = Number(edge.lineWidth) || self.config.link.lineWidth; + link.strokeColor = self.getColor(edge, self.config.link.color); + link.weight = Number(edge.weight) || 1; + link.lineType = edge.lineType || self.config.link.lineType; + link.lineDash = edge.lineDash || self.config.link.lineDash; + link.font = edge.font || self.config.link.label.font; + link.fontColor = edge.fontColor || self.config.link.label.color; + link.properties = edge.properties || {}; + } + }; + VisualGraph.prototype.getGraphData = function () { + var self = this; + return { nodes: self.nodes, links: self.links }; + }; + VisualGraph.prototype.getVisibleData = function () { + var self = this; + var visibleNodes = self.nodes.filter(function (n) { + return n.visible == true; + }); + var visibleLinks = self.links.filter(function (l) { + var source = l.source, + target = l.target; + return source.visible == true && target.visible == true; + }); + visibleNodes.forEach((n) => { + n.fixed = false; + }); + return { nodes: visibleNodes, links: visibleLinks }; + }; + VisualGraph.prototype.setZoom = function (type) { + var self = this; + if (type == "zoomOut") { + self.stage.zoomOut(); + } else if (type == "zoomIn") { + self.stage.zoomIn(); + } else if (type == "zoom1") { + self.stage.centerAndZoom(); + self.scene.scaleX = 1; + self.scene.scaleY = 1; + } else { + self.stage.centerAndZoom(); + } + self.refresh(); + }; + VisualGraph.prototype.setZoomRange = function (range = [0.1, 10]) { + if (range instanceof Array && range.length == 2) { + var min = Number(range[0]) || 0.1; + var max = Number(range[1]) || 10; + if (min < max) { + this.scene.scaleRange = [min, max]; + } + } + }; + VisualGraph.prototype.moveCenter = function (scale = 1) { + this.scene.scaleX = scale; + this.scene.scaleY = scale; + this.scene.translateToCenter(); + this.refresh(); + }; + VisualGraph.prototype.moveNodeToCenter = function (node, times) { + var self = this; + if (times != null && Number(times) > 0) { + self.scene.translateX = -node.x + self.stage.width / 2; + self.scene.translateY = -node.y + self.stage.height / 2; + } else { + self.scene.setCenter(node.x, node.y); + } + self.refresh(); + }; + VisualGraph.prototype.moveScene = function (direct = "left", distance = 20) { + var self = this; + switch (direct) { + case "left": + self.scene.translateX -= Number(distance); + break; + case "right": + self.scene.translateX += Number(distance); + break; + case "up": + self.scene.translateY -= Number(distance); + break; + case "down": + self.scene.translateY += Number(distance); + break; + default: + break; + } + }; + VisualGraph.prototype.setLineType = function (type) { + this.links.forEach(function (link) { + link.lineType = type; + }); + this.refresh(); + }; + VisualGraph.prototype.setNodeShape = function (type) { + this.nodes.forEach(function (node) { + if (node.shape == "circle") { + node.height = node.width; + } + node.shape = type; + }); + this.refresh(); + }; + VisualGraph.prototype.showNodeLabel = function (flag) { + this.nodes.forEach(function (node) { + node.showlabel = true; + }); + this.refresh(); + }; + VisualGraph.prototype.showLinkLabel = function (flag) { + this.links.forEach(function (link) { + link.showlabel = true; + }); + this.refresh(); + }; + VisualGraph.prototype.resize = function () { + this.stage.resize(); + }; + VisualGraph.prototype.setMouseModel = function (model, type) { + if (model == "drag") { + this.stage.mode = "drag"; + } else if (model == "select") { + this.stage.mode = "select"; + if (type && ["rect", "lasso"].indexOf(type) != -1) { + this.scene.selectBoxType = type; + } + } else { + this.stage.mode = "normal"; + } + }; + VisualGraph.prototype.contract = function (curNode) { + var _self = this; + if (curNode) { + var leafNodes = []; + (curNode.outLinks || []).forEach(function (l) { + if ( + (l.target.outLinks || []).length == 0 && + (l.target.inLinks || []).length == 1 + ) { + leafNodes.push(l.target); + l.visible = false; + } + }); + curNode.tipText = leafNodes.length; + leafNodes.forEach(function (n) { + n.visible = false; + }); + _self.refresh(); + } + }; + VisualGraph.prototype.expanded = function (curNode) { + var _self = this; + if (curNode) { + var targetNodes = []; + (curNode.outLinks || []).forEach(function (l) { + var target = l.target; + if ((target.outLinks || []).length == 0 && target.visible == false) { + l.visible = true; + target.visible = true; + targetNodes.push(target); + } + }); + if (targetNodes.length == 0) { + return; + } + curNode.tipText = null; + } + }; + VisualGraph.prototype.saveImage = function ( + config = { + width: 6000, + height: 6000, + type: "png", + fileName: "picture", + background: "#fff", + textWatermark: { + content: "", + angle: -30, + alpha: 0.2, + fontStyle: "bold", + fontSize: 60, + fontFamliy: "Arial", + fontColor: "#666", + }, + }, + ) { + this.stage.saveAsLocalImage(config); + }; + VisualGraph.prototype.exportJsonFile = function (fileName) { + var jsonStr = JSON.stringify(this.serialized()); + funDownload(jsonStr, fileName || "graphvis" + ".json"); + function funDownload(content, filename) { + var blob = new Blob([content], { type: "text/json" }); + let a = document.createElement("a"); + let event = new MouseEvent("click"); + a.download = filename; + a.href = window.URL.createObjectURL(blob); + a.dataset.downloadurl = ["text/json", a.download, a.href].join(":"); + a.dispatchEvent(event); + } + }; + VisualGraph.prototype.showOverView = function (flag) { + if (flag) { + this.stage.thumbnail.show(); + } else { + this.stage.thumbnail.destory(); + } + }; + VisualGraph.prototype.findNode = function (nodeLabel) { + var nodes = this.nodes.filter(function (n) { + if (n.label == null) return false; + return n.label + "" == nodeLabel || n.id == nodeLabel; + }); + if (nodes.length > 0) { + var node = nodes[0]; + node.selected = true; + node.showlabel = true; + this.currentNode = node; + this.focusTargetEle(node); + return node; + } + return null; + }; + VisualGraph.prototype.focusTargetEle = function (ele, params, callback) { + ele.selected = true; + if (!params) { + params = {}; + } + if (!params.x && params.x != 0) { + params.x = this.stage.width / 2; + } + if (!params.y && params.y != 0) { + params.y = this.stage.height / 2; + } + this.scene.translateX = -ele.x + params.x; + this.scene.translateY = -ele.y + params.y; + this.refresh(); + callback && callback(); + }; + VisualGraph.prototype.converHexToRGB = function (hex) { + var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; + hex = hex.replace(shorthandRegex, function (m, r, g, b) { + return r + r + g + g + b + b; + }); + var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if (result) { + return ( + parseInt(result[1], 16) + + "," + + parseInt(result[2], 16) + + "," + + parseInt(result[3], 16) + ); + } + return "150,150,200"; + }; + VisualGraph.prototype.setLabelColor = function (hexColor) { + var rgbColor = this.converHexToRGB(hexColor); + this.config.node.label.color = rgbColor; + this.nodes.forEach(function (node) { + node.fontColor = rgbColor; + }); + this.refresh(); + }; + VisualGraph.prototype.hideAllLink = function () { + this.showLinkFlag = false; + this.links.forEach(function (link) { + link.visible = false; + }); + this.refresh(); + }; + VisualGraph.prototype.showAllLink = function () { + this.showLinkFlag = true; + this.links.forEach(function (link) { + if (link.source.visible && link.target.visible) { + link.visible = true; + } + }); + this.refresh(); + }; + VisualGraph.prototype.showAllNode = function () { + this.nodes.forEach(function (node) { + node.alpha = 1; + node.visible = true; + }); + this.refresh(); + }; + VisualGraph.prototype.setNodeFont = function (fontSize) { + var self = this; + self.nodes.forEach(function (n) { + n.font = "normal " + fontSize + "px Arial"; + }); + self.refresh(); + }; + VisualGraph.prototype.setTextPosition = function (textAlign) { + var self = this; + self.nodes.forEach(function (n) { + n.textPosition = textAlign; + }); + self.refresh(); + }; + VisualGraph.prototype.setLinkAlpha = function (alpha) { + this.links.forEach(function (links) { + links.alpha = alpha; + }); + this.refresh(); + }; + VisualGraph.prototype.setLinkArrowShow = function (flag) { + this.links.forEach(function (l) { + l.showArrow = flag; + }); + this.refresh(); + }; + VisualGraph.prototype.setLinkColor = function (hexColor) { + var rgbColor = this.converHexToRGB(hexColor); + this.links.forEach(function (link) { + link.colorType = "defined"; + link.strokeColor = rgbColor; + }); + this.refresh(); + }; + VisualGraph.prototype.setLinkColorType = function (type) { + var linkColorType = type || "source"; + this.links.forEach(function (l) { + l.colorType = linkColorType; + if (linkColorType == "defined") { + l.strokeColor = self.config.link.color || "50,50,50"; + } + }); + this.refresh(); + }; + VisualGraph.prototype.setLinkFont = function (fontSize) { + var self = this; + self.links.forEach(function (l) { + l.font = "normal " + fontSize + "px Arial"; + l.textOffsetX = -Number(fontSize); + }); + this.refresh(); + }; + VisualGraph.prototype.setLinkLabelColor = function (hexColor) { + var self = this; + var rgbColor = this.converHexToRGB(hexColor); + self.links.forEach(function (l) { + l.fontColor = rgbColor; + }); + this.refresh(); + }; + VisualGraph.prototype.resetNodeInfo = function (nodeInfo) { + var node = this.currentNode; + if (node) { + node.label = this.currentNode.text = nodeInfo.name; + node.width = Number(nodeInfo.width) || Number(nodeInfo.size) || 60; + node.height = Number(nodeInfo.height) || Number(nodeInfo.size) || 60; + node.scaleX = node.scaleY = Number(nodeInfo.scale) || 1; + node.fillColor = this.converHexToRGB(nodeInfo.fillColor); + node.shape = nodeInfo.shape; + node.showlabel = true; + node.imageUrl = nodeInfo.image; + node.setImage(nodeInfo.image); + this.refresh(); + } + }; + VisualGraph.prototype.resetEdgeStyle = function (lineInfo) { + var self = this; + var line = self.currentLink; + if (line) { + line.label = lineInfo["label"]; + line.text = line.label; + line.type = line.label; + line.lineWidth = lineInfo["lineWidth"]; + line.lineType = lineInfo["lineType"]; + if (lineInfo["lineDash"]) { + line.lineDash = lineInfo["lineDash"].split(","); + } + var color = lineInfo["edgeColor"]; + if (color && color.length > 0) { + line.strokeColor = self.converHexToRGB(color); + line.fontColor = line.strokeColor; + line.colorType = "defined"; + } + line.showlabel = true; + this.refresh(); + } + }; + VisualGraph.prototype.addClusterContainer = function ( + clusters, + avoidOverlap, + ) { + var self = this; + if (clusters == null) { + return; + } + if (avoidOverlap == undefined || avoidOverlap == null) { + avoidOverlap = false; + } + self.clusterGroups.forEach(function (group) { + self.scene.remove(group); + }); + self.clusterGroups = []; + for (var clusterKey in clusters) { + var cluster = clusters[clusterKey]; + var clusterNodes = self.nodes.filter(function (node) { + return node.cluster == clusterKey; + }); + var size = cluster.size, + rate = cluster.rate, + color = cluster.color; + if (clusterNodes.length > 1) { + var group = new DGraph.Group(); + group.headerColor = color; + group.fillColor = color; + group.alpha = 0.3; + group.label = clusterKey; + group.mouseup(function (evt) { + if (evt.button == 2) { + self.currentCluster = this; + self.showClusterRightMenu(evt, this); + } + }); + self.scene.add(group); + self.clusterGroups.push(group); + clusterNodes.forEach(function (node) { + group.add(node); + }); + this.refresh(); + } + } + if (!avoidOverlap) { + self.clusterAvoidOverlap(self.clusterGroups); + this.refresh(); + } + }; + VisualGraph.prototype.removeCluster = function () { + var self = this; + self.clusterGroups = self.clusterGroups.filter(function (clusterGroup) { + return clusterGroup != self.currentCluster; + }); + self.scene.remove(self.currentCluster); + this.refresh(); + }; + VisualGraph.prototype.clearClusters = function () { + var self = this; + self.clusterGroups.forEach(function (clusterGroup) { + self.scene.remove(clusterGroup); + }); + self.clusterGroups = []; + this.refresh(); + }; + VisualGraph.prototype.clusterAvoidOverlap = function (clusterGroups) { + var self = this; + var virtualNodes = []; + clusterGroups.forEach(function (group, i) { + group.ajustSize(); + var groupRadius = Math.sqrt( + (group.width / 2) * (group.width / 2) + + (group.height / 2) * (group.height / 2), + ); + var tempNode = new DGraph.Node(); + tempNode.radius = groupRadius; + tempNode.x = group.cx; + tempNode.y = group.cy; + virtualNodes[i] = tempNode; + }); + if (typeof LayoutFactory != "undefined") { + var layout = new LayoutFactory({ + nodes: virtualNodes, + links: null, + }).createLayout("noverlap"); + if (layout != null) { + layout.initAlgo(); + layout.resetConfig({ maxMove: 1 }); + var times = 0, + runFlag = true; + while (times++ < 300 && runFlag) { + layout.runLayout(); + runFlag = layout.runFlag; + } + } + } + virtualNodes.forEach(function (node, i) { + var group = clusterGroups[i]; + var dx = node.x - group.cx, + dy = node.y - group.cy; + group.childs.forEach(function (childNode) { + childNode.x += dx; + childNode.y += dy; + }); + }); + }; + VisualGraph.prototype.applyNodeSize = function (range) { + var self = this; + var minDegree = 0; + var maxDegree = 0; + self.nodes.forEach(function (n) { + var degree = (n.outLinks || []).length + (n.inLinks || []).length; + n.degree = degree; + minDegree = Math.min(degree, minDegree); + maxDegree = Math.max(degree, maxDegree); + }); + var degreesDomain = [Math.sqrt(minDegree), Math.sqrt(maxDegree)]; + self.nodes.forEach(function (n) { + var scale = self.numScale(range, degreesDomain, Math.sqrt(n.degree)); + n.scaleX = scale; + n.scaleY = scale; + }); + this.refresh(); + }; + VisualGraph.prototype.applyLinkWeight = function (range) { + var self = this; + var minWeight = 3; + var maxWeight = 8; + self.links.forEach(function (l) { + minWeight = Math.min(l.weight, minWeight); + maxWeight = Math.max(l.weight, maxWeight); + }); + var weightDomain = [Math.sqrt(minWeight), Math.sqrt(maxWeight)]; + self.links.forEach(function (l) { + var lineWidth = self.numScale(range, weightDomain, Math.sqrt(l.weight)); + l.lineWidth = Math.round(lineWidth); + }); + this.refresh(); + }; + VisualGraph.prototype.numScale = function (range, domain, num) { + return ( + ((num - domain[0]) * (range[1] - range[0])) / (domain[1] - domain[0]) + + range[0] + ).toFixed(1); + }; + VisualGraph.prototype.selectAll = function () { + var _self = this; + this.nodes.forEach(function (n) { + n.selected = true; + _self.scene.addToSelected(n); + }); + this.refresh(); + }; + VisualGraph.prototype.reverseSelect = function () { + var _self = this; + this.nodes.forEach(function (n) { + if (n.selected) { + n.selected = false; + _self.scene.removeFromSelected(n); + } else { + n.selected = true; + _self.scene.addToSelected(n); + } + }); + this.refresh(); + }; + VisualGraph.prototype.selectRelate = function (node) { + var self = this; + var _node = node || self.currentNode; + if (_node == null) { + return false; + } + var inLinks = _node.inLinks || []; + var outLinks = _node.outLinks || []; + inLinks.forEach(function (link) { + link.source.selected = true; + self.scene.addToSelected(link.source); + }); + outLinks.forEach(function (link) { + link.target.selected = true; + self.scene.addToSelected(link.target); + }); + this.refresh(); + }; + VisualGraph.prototype.showSelected = function () { + this.nodes.forEach(function (n) { + if (!n.selected) { + n.visible = false; + var inLinks = n.inLinks || [], + outLinks = n.outLinks || []; + inLinks.forEach(function (l) { + l.visible = false; + }); + outLinks.forEach(function (l) { + l.visible = false; + }); + } + }); + this.refresh(); + }; + VisualGraph.prototype.setScale = function (node, scale) { + if (node) { + node.scaleX = scale; + node.scaleY = scale; + this.refresh(); + } + }; + VisualGraph.prototype.hideSelected = function () { + this.nodes.forEach(function (n) { + if (n.selected) { + n.visible = false; + var inLinks = n.inLinks || [], + outLinks = n.outLinks || []; + inLinks.forEach(function (l) { + l.visible = false; + }); + outLinks.forEach(function (l) { + l.visible = false; + }); + } + }); + this.refresh(); + }; + VisualGraph.prototype.hideIsolatedNodes = function () { + this.nodes.forEach(function (n) { + if ((n.inLinks || []).length == 0 && (n.outLinks || []).length == 0) { + n.visible = false; + } + }); + this.refresh(); + }; + VisualGraph.prototype.showNodes = function () { + this.nodes.forEach(function (n) { + n.visible = true; + n.alpha = 1; + }); + this.refresh(); + }; + VisualGraph.prototype.deleteNodes = function (nodes) { + if ((!nodes) instanceof Array || nodes.length == 0) { + return; + } + var self = this; + var links = []; + var index = -1; + nodes.forEach((node) => { + (node.inLinks || []).forEach(function (l) { + links.push(l); + }); + (node.outLinks || []).forEach(function (l) { + links.push(l); + }); + index = self.nodes.indexOf(node); + if (index > -1) { + self.nodes.splice(index, 1); + self.scene.remove(node); + } + }); + links.forEach(function (l) { + index = self.links.indexOf(l); + if (index > -1) { + self.links.splice(index, 1); + self.scene.remove(l); + } + }); + this.refresh(); + }; + VisualGraph.prototype.deleteNode = function (node) { + if (node) { + this.deleteNodes([node]); + } + }; + VisualGraph.prototype.deleteLinks = function (links) { + if ((!links) instanceof Array || links.length == 0) { + return; + } + var self = this; + links.forEach((link) => { + var index = self.links.indexOf(link); + if (index > -1) { + self.links.splice(index, 1); + self.scene.remove(link); + } + }); + this.refresh(); + }; + VisualGraph.prototype.deleteLink = function (link) { + if (link) { + this.deleteLinks([link]); + } + }; + VisualGraph.prototype.setNodeLabelWithDegree = function (degree) { + this.nodes.forEach(function (n) { + if ((n.inLinks || []).length + (n.outLinks || []).length >= degree) { + n.showlabel = true; + } else { + n.showlabel = true; + n.text = null; + } + }); + this.refresh(); + }; + VisualGraph.prototype.translateOrZoom = function (type) { + var self = this; + if (type === "zoomOut") { + scaleGraph(1.1); + } else if (type === "zoomIn") { + scaleGraph(0.9); + } else { + this.stage.centerAndZoom(1.0, 1.0); + } + this.refresh(); + function scaleGraph(scale) { + var nodeCount = self.nodes.length; + var xMean = 0, + yMean = 0; + self.nodes.forEach(function (n) { + xMean += n.x; + yMean += n.y; + }); + xMean /= nodeCount; + yMean /= nodeCount; + self.nodes.forEach(function (n) { + var dx = (n.x - xMean) * scale; + var dy = (n.y - yMean) * scale; + n.x = xMean + dx; + n.y = yMean + dy; + }); + } + }; + VisualGraph.prototype.rotateGraph = function (angle) { + var self = this; + var sin = Math.sin((-angle * Math.PI) / 360); + var cos = Math.cos((-angle * Math.PI) / 360); + var bounds = this.stage.getBound(); + var px = Math.round(bounds.width / 2); + var py = Math.round(bounds.height / 2); + this.nodes.forEach(function (n) { + var dx = n.x - px; + var dy = n.y - py; + n.x = px + dx * cos - dy * sin; + n.y = py + dy * cos + dx * sin; + }); + this.refresh(); + }; + VisualGraph.prototype.getGraphStatistic = function () { + var self = this; + if (self.nodes.length == 0) { + return null; + } + return { + nodesCount: self.nodes.length, + linksCount: self.links.length, + density: self.calculateDensity(), + avgDegree: self.calculateAvgDegree(), + avgWeightDegree: self.calculateAvgWieghtDegree(), + }; + }; + VisualGraph.prototype.createMatrix = function (graphVertex, graphAdge) { + var n = graphVertex.length; + var matrix = []; + for (let i = 0; i < n; i++) { + let arrTemp = new Array(n); + for (let j = 0; j < n; j++) { + arrTemp[j] = Infinity; + } + matrix[i] = arrTemp; + } + var e = graphAdge.length; + for (let i = 0; i < e; i++) { + var sourceInx = graphVertex.indexOf(graphAdge[i].source); + var targetInx = graphVertex.indexOf(graphAdge[i].target); + if (sourceInx === -1 || targetInx === -1) { + return; + } + matrix[sourceInx][targetInx] = graphAdge[i].weight; + matrix[targetInx][sourceInx] = graphAdge[i].weight; + } + return matrix; + }; + VisualGraph.prototype.graphAlgorithm = function () { + var vertex = this.nodes.map(function (node) { + return node.id; + }); + var edges = this.links.map(function (link) { + return { source: link.source.id, target: link.target.id, weight: 1 }; + }); + var matrix = this.createMatrix(vertex, edges); + var n = vertex.length; + var dfsTraverse = function (v) { + var visited = []; + var result = []; + var stack = []; + stack.push(v); + result.push(v); + visited[vertex.indexOf(v)] = true; + while (stack.length !== 0) { + let p = stack.pop(); + if (visited[vertex.indexOf(p)] !== true) { + result.push(p); + } + visited[vertex.indexOf(p)] = true; + let row = vertex.indexOf(p); + for (let i = n - 1; i >= 0; i--) { + if (matrix[row][i] !== Infinity && visited[i] !== true) { + stack.push(vertex[i]); + } + } + if (result.length > 100) { + break; + } + } + return result; + }; + var bfsTraverse = function (v) { + var queue = []; + var visited = []; + var pre = 0, + tail = 1; + queue.push(v); + visited[vertex.indexOf(v)] = true; + while (pre !== tail) { + let p = queue[pre++]; + let row = vertex.indexOf(p); + for (let i = 0; i < n; i++) { + if (matrix[row][i] !== Infinity && visited[i] !== true) { + queue.push(vertex[i]); + visited[i] = true; + } + } + tail = queue.length; + if (pre > 100) { + break; + } + } + return queue; + }; + var floyd = function () { + var dist = [], + path = []; + for (let i = 0; i < n; i++) { + dist[i] = new Array(); + path[i] = new Array(); + for (let j = 0; j < n; j++) { + dist[i][j] = matrix[i][j]; + path[i][j] = [vertex[i]]; + } + } + for (let k = 0; k < n; k++) { + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (dist[i][k] + dist[k][j] < dist[i][j]) { + dist[i][j] = dist[i][k] + dist[k][j]; + path[i][j] = path[i][k].concat(path[k][j]); + } + if (i === j) { + dist[i][j] = 0; + } + } + } + } + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + path[i][j].push(vertex[j]); + } + } + var out = { dist: dist, path: path }; + return out; + }; + var dijkstra = function (v) { + var dist = [], + path = []; + for (let i = 0; i < n; i++) { + dist[i] = matrix[vertex.indexOf(v)][i]; + if (dist[i] !== Infinity) { + path[i] = [v, vertex[i]]; + } else { + path[i] = []; + } + } + var vis = []; + vis[vertex.indexOf(v)] = true; + dist[vertex.indexOf(v)] = 0; + var num = 1; + while (num < n) { + let k = 1; + for (let i = 0; i < n; i++) { + if (vis[i] !== true && dist[k] > dist[i]) { + k = i; + } + } + vis[k] = true; + for (let i = 0; i < n; i++) { + if (dist[i] > dist[k] + matrix[k][i]) { + dist[i] = dist[k] + matrix[k][i]; + path[i] = path[k].concat([vertex[i]]); + } + } + num++; + } + var out = { dist: dist, path: path }; + return out; + }; + var bellmanFord = function (source) { + let distance = new Map(); + let paths = new Map(); + for (let v of vertex) { + distance.set(v, Infinity); + paths.set(v, []); + } + distance.set(source, 0); + for (let i = 1, len = vertex.length - 1; i < len; i++) { + for (let e of edges) { + if (distance.get(e.source) + e.weight < distance.get(e.target)) { + distance.set(e.target, distance.get(e.source) + e.weight); + var pathArr = paths.get(e.target); + pathArr = pathArr.concat(paths.get(e.source)); + pathArr.push(e.source); + paths.set(e.target, pathArr); + } + } + } + paths.forEach((path, node) => { + if (path.length > 0) { + path.push(node); + } + }); + return { dist: distance, paths: paths }; + }; + return { + dfsTraverse: dfsTraverse, + bfsTraverse: bfsTraverse, + floyd: floyd, + dijkstra: dijkstra, + bellmanFord: bellmanFord, + }; + }; + VisualGraph.prototype.calculateDensity = function () { + var self = this; + var nodesCount = self.nodes.length, + linksCount = self.links.length; + var multiplier = 1; + if (!self.isDerictedGraph) { + multiplier = 2; + } + var density = + (multiplier * linksCount) / (nodesCount * nodesCount - nodesCount); + return density.toFixed(4); + }; + VisualGraph.prototype.calculateAvgDegree = function () { + var self = this; + var nodesCount = self.nodes.length, + averageDegree = 0; + self.nodes.forEach(function (n) { + averageDegree += (n.inLinks || []).length + (n.outLinks || []).length; + }); + var multiplier = 1; + if (self.isDerictedGraph) { + multiplier = 2; + } + var avgDegree = (averageDegree /= multiplier * nodesCount); + return avgDegree.toFixed(4); + }; + VisualGraph.prototype.calculateDarmity = function () { + var self = this; + let darmity = 0; + var algo = self.graphAlgorithm(); + var distArr = algo.floyd().dist; + distArr.forEach((distLine) => { + distLine.forEach((dist) => { + darmity = Math.max(dist, darmity); + }); + }); + return darmity; + }; + VisualGraph.prototype.avgClusterCoefficient = function () { + var self = this; + if (self.nodes.length == 0) { + return 0; + } + let clusterCoefficient = 0; + self.nodes.forEach(function (node) { + clusterCoefficient += self.clusterCoefficient(node); + }); + return (clusterCoefficient / self.nodes.length).toFixed(4); + }; + VisualGraph.prototype.avgPathLength = function () { + var self = this; + if (self.nodes.length == 0) { + return 0; + } + var totalPath = 0; + var pathNum = 0; + var algo = self.graphAlgorithm(); + var distArr = algo.floyd().dist; + distArr.forEach((distLine) => { + distLine.forEach((dist) => { + if (dist !== Infinity && dist > 0) { + pathNum++; + totalPath += dist; + } + }); + }); + return (totalPath / pathNum).toFixed(4); + }; + VisualGraph.prototype.calculateAvgWieghtDegree = function () { + var self = this; + var nodesCount = self.nodes.length, + averageWeightDegree = 0; + self.nodes.forEach(function (n) { + if (self.isDerictedGraph) { + (n.inLinks || []).forEach(function (l) { + averageWeightDegree += l.weight; + }); + (n.outLinks || []).forEach(function (l) { + averageWeightDegree += l.weight; + }); + } else { + var allLinks = []; + allLinks.push(n.inLinks || []); + allLinks.push(n.outLinks || []); + allLinks.forEach(function (l) { + var multi = 1; + if (l.source == l.target) { + multi = 2; + } + averageWeightDegree += multi * l.weight; + }); + } + }); + var multiplier = 1; + if (self.isDerictedGraph) { + multiplier = 2; + } + var avgDegree = (averageWeightDegree /= multiplier * nodesCount); + return avgDegree.toFixed(4); + }; + VisualGraph.prototype.closenessCentrality = function (node, type) { + var self = this; + let sumShortPath = 0; + var algo = self.graphAlgorithm(); + var shortPaths = algo.dijkstra(node.id); + shortPaths.dist.forEach((pathLength) => { + if (pathLength !== Infinity) { + sumShortPath += pathLength; + } + }); + var closeness = (self.nodes.length - 1) / sumShortPath; + return closeness; + }; + VisualGraph.prototype.degreeCentrality = function (node) { + var self = this; + var nodeNum = self.nodes.length; + if (nodeNum == 1) { + return 0; + } + var degreeCentrality = + ((node.outLinks || []).length + (node.inLinks || []).length) / + (nodeNum - 1); + return degreeCentrality; + }; + VisualGraph.prototype.clusterCoefficient = function (node) { + var self = this; + var nodeIdSet = new Set(); + var outNeiberNodes = (node.outLinks || []).map((link) => { + return link.target; + }); + var inNeiberNodes = (node.inLinks || []).map((link) => { + return link.source; + }); + var allNeiberNodes = outNeiberNodes.concat(inNeiberNodes); + allNeiberNodes.forEach((_node) => { + if (_node.id != node.id) { + nodeIdSet.add(_node.id); + } + }); + var allNeiberLinkSet = new Set(); + allNeiberNodes.forEach((_node) => { + var outLinks = (_node.outLinks || []).filter((_link) => { + return _link.target.id != node.id && _link.source != _link.target; + }); + var nodeIdStr1 = null, + nodeIdStr2 = null; + outLinks.forEach((_link) => { + if (nodeIdSet.has(_link.source.id) && nodeIdSet.has(_link.target.id)) { + nodeIdStr1 = _link.source.id + ":" + _link.target.id; + nodeIdStr2 = _link.target.id + ":" + _link.source.id; + if ( + !allNeiberLinkSet.has(nodeIdStr1) && + !allNeiberLinkSet.has(nodeIdStr2) + ) { + allNeiberLinkSet.add(nodeIdStr1); + } + } + }); + var inLinks = (_node.inLinks || []).filter((_link) => { + return _link.source.id != node.id && _link.source != _link.target; + }); + inLinks.forEach((_link) => { + if (nodeIdSet.has(_link.source.id) && nodeIdSet.has(_link.target.id)) { + nodeIdStr1 = _link.source.id + ":" + _link.target.id; + nodeIdStr2 = _link.target.id + ":" + _link.source.id; + if ( + !allNeiberLinkSet.has(nodeIdStr1) && + !allNeiberLinkSet.has(nodeIdStr2) + ) { + allNeiberLinkSet.add(nodeIdStr1); + } + } + }); + }); + var edgeNum = allNeiberLinkSet.size; + var ck12 = nodeIdSet.size * (nodeIdSet.size - 1); + var clusterCoefficient = 0; + if (ck12 > 0) { + clusterCoefficient = edgeNum / ck12; + } + return clusterCoefficient; + }; + VisualGraph.prototype.edgeBetweennessCentrality = function (edge) { + var self = this; + let allShortPath = 0; + let processShortPath = 0; + var sourceId = edge.source.id; + var targetId = edge.target.id; + var algo = self.graphAlgorithm(); + var paths = algo.floyd().path; + var arrLength = 0, + startNodeId = null, + endNodeId = null; + var sourceNodeIndx, targetNodeIndx; + paths.forEach((pathArr) => { + pathArr.forEach((path) => { + arrLength = path.length; + startNodeId = path[0]; + endNodeId = path[arrLength - 1]; + if (startNodeId != endNodeId) { + allShortPath++; + sourceNodeIndx = path.indexOf(sourceId); + targetNodeIndx = path.indexOf(targetId); + if ( + sourceNodeIndx != -1 && + targetNodeIndx != -1 && + sourceNodeIndx + 1 == targetNodeIndx + ) { + processShortPath++; + } + } + }); + }); + if (allShortPath == 0) { + return 0; + } + return (processShortPath / allShortPath) * 2; + }; + VisualGraph.prototype.rankEdgeBetweennessCentrality = function () { + var self = this; + var algo = self.graphAlgorithm(); + var paths = algo.floyd().path; + var edges = self.links.map(function (link) { + if (!link.id) { + link.id = link.source.id + "@" + link.target.id; + } + return link; + }); + edges.forEach((edge) => { + var allShortPath = 0; + var processShortPath = 0; + var sourceId = edge.source.id; + var targetId = edge.target.id; + var arrLength = 0, + startNodeId = null, + endNodeId = null; + var sourceNodeIndx, targetNodeIndx; + paths.forEach((pathArr) => { + pathArr.forEach((path) => { + arrLength = path.length; + startNodeId = path[0]; + endNodeId = path[arrLength - 1]; + if (startNodeId != endNodeId) { + allShortPath++; + sourceNodeIndx = path.indexOf(sourceId); + targetNodeIndx = path.indexOf(targetId); + if ( + sourceNodeIndx != -1 && + targetNodeIndx != -1 && + sourceNodeIndx + 1 == targetNodeIndx + ) { + processShortPath++; + } + } + }); + }); + if (allShortPath == 0) { + edge.betweennessCentrality = 0; + } else { + edge.betweennessCentrality = (processShortPath / allShortPath) * 2; + } + }); + edges.sort(function (edge1, edge2) { + return edge2.betweennessCentrality - edge1.betweennessCentrality; + }); + return edges; + }; + VisualGraph.prototype.serialized = function () { + var self = this; + var _graph = { nodes: [], links: [] }; + self.nodes.forEach(function (n) { + _graph.nodes.push({ + id: n.id, + label: n.label, + x: Math.round(n.x), + y: Math.round(n.y), + width: n.width, + height: n.height, + size: n.size, + color: n.fillColor, + shape: n.shape, + }); + }); + self.links.forEach(function (l) { + _graph.links.push({ + id: l.id, + source: l.source.id, + target: l.target.id, + label: l.label || l.type || "", + lineWidth: l.lineWidth, + lineType: l.lineType, + color: l.strokeColor, + }); + }); + return _graph; + }; + VisualGraph.prototype.nodeWrapText = function (flag) { + this.nodes.forEach((node) => { + node.wrapText = flag; + }); + this.refresh(); + }; + VisualGraph.prototype.nodeMapAlphaByDegree = function () { + var self = this; + var minDegree = 0, + maxDegree = 0; + self.nodes.forEach(function (n) { + n.outdegree = (n.outLinks || []).reduce(function (total, _link) { + return total + (_link.weight || 1); + }, 0); + n.indegree = (n.inLinks || []).reduce(function (total, _link) { + return total + (_link.weight || 1); + }, 0); + n.degree = n.outdegree + n.indegree; + minDegree = Math.min(n.degree, minDegree); + maxDegree = Math.max(n.degree, maxDegree); + }); + var degreesDomain = [minDegree, maxDegree]; + var range = [0.2, 1]; + self.nodes.forEach(function (n) { + var _alpha = self.numScale(range, degreesDomain, n.degree); + n.alpha = _alpha; + (n.outLinks || []).forEach(function (l) { + l.alpha = _alpha; + }); + }); + this.refresh(); + }; + VisualGraph.prototype.nodeMapSizeByDegree = function (propType, range) { + var self = this; + var minDegree = 0, + maxDegree = 0; + var degreeArr = {}; + self.nodes.forEach(function (n) { + n.outdegree = (n.outLinks || []).reduce(function (total, _link) { + return total + (_link.weight || 1); + }, 0); + n.indegree = (n.inLinks || []).reduce(function (total, _link) { + return total + (_link.weight || 1); + }, 0); + n.degree = n.outdegree + n.indegree; + if (propType == "degree") { + minDegree = Math.min(n.degree, minDegree); + maxDegree = Math.max(n.degree, maxDegree); + } else if (propType == "outdegree") { + minDegree = Math.min(n.outdegree, minDegree); + maxDegree = Math.max(n.outdegree, maxDegree); + } else { + minDegree = Math.min(n.indegree, minDegree); + maxDegree = Math.max(n.indegree, maxDegree); + } + }); + var degreesDomain = [minDegree, maxDegree]; + self.nodes.forEach(function (n) { + var nodeSize = 1; + if (propType == "degree") { + nodeSize = self.numScale(range, degreesDomain, n.degree); + } else if (propType == "outdegree") { + nodeSize = self.numScale(range, degreesDomain, n.outdegree); + } else { + nodeSize = self.numScale(range, degreesDomain, n.indegree); + } + n.width = n.height = n.radius = Math.round(nodeSize); + }); + this.refresh(); + }; + VisualGraph.prototype.findAllPath = function ( + startNode, + endNode, + isDirected, + ) { + var self = this; + if (!startNode || !endNode) { + return []; + } + let nodeVisited = new Map(); + let allPathNodes = []; + const findPath = (source, target, pathNodes = []) => { + pathNodes = [...pathNodes]; + pathNodes.push(source); + if (source === target) { + allPathNodes.push(pathNodes); + return; + } + const neighborNodes = []; + (source.outLinks || []).forEach((link) => { + if (neighborNodes.indexOf(link.target) == -1 && source != link.target) { + neighborNodes.push(link.target); + } + }); + if (!isDirected) { + (source.inLinks || []).forEach((link) => { + if ( + neighborNodes.indexOf(link.source) == -1 && + source != link.source + ) { + neighborNodes.push(link.source); + } + }); + } + for (let i = 0; i < neighborNodes.length; i++) { + var node = neighborNodes[i]; + if (pathNodes.indexOf(node) == -1) { + findPath(node, target, pathNodes); + } + } + }; + findPath(startNode, endNode); + allPathNodes.sort((path1, path2) => { + return path1.length - path2.length; + }); + let allPathLinks = []; + allPathNodes.forEach((pathNodes) => { + var paths = []; + var length = pathNodes.length; + for (var i = 0; i < length; i++) { + var pathNode = pathNodes[i]; + (pathNode.outLinks || []).forEach((link) => { + if ( + pathNodes.indexOf(link.target) != -1 && + pathNodes.indexOf(link.source) != -1 && + paths.indexOf(link) == -1 + ) { + paths.push(link); + } + }); + (pathNode.inLinks || []).forEach((link) => { + if ( + pathNodes.indexOf(link.target) != -1 && + pathNodes.indexOf(link.source) != -1 && + paths.indexOf(link) == -1 + ) { + paths.push(link); + } + }); + } + allPathLinks.push(paths); + }); + return allPathLinks; + }; + VisualGraph.prototype.findShortPath = function ( + sourceId, + targetId, + direction, + ) { + var self = this; + var startNode = self.nodes.filter(function (n) { + return n.id == sourceId; + })[0]; + var endNode = self.nodes.filter(function (n) { + return n.id == targetId; + })[0]; + let allPathLinks = []; + if (direction == "source") { + allPathLinks = self.findAllPath(startNode, endNode, true); + } else if (direction == "target") { + allPathLinks = self.findAllPath(endNode, startNode, true); + } else { + allPathLinks = self.findAllPath(startNode, endNode, false); + } + var shortPaths = []; + if (allPathLinks.length > 0) { + var shortestPathLength = allPathLinks[0].length; + shortPaths = allPathLinks.filter((pathArr) => { + return pathArr.length == shortestPathLength; + }); + shortPaths.forEach((paths) => { + paths.forEach((link) => { + self.selectedEdge(link); + }); + }); + self.refresh(); + } + return shortPaths; + }; + VisualGraph.prototype.pathAnalyze = function (sourceId, targetId, direction) { + var self = this; + if (sourceLabel.length == 0 || targetLabel.length == 0) { + return null; + } + var startNode = self.nodes.filter(function (n) { + return n.id == sourceId; + })[0]; + var endNode = self.nodes.filter(function (n) { + return n.id == targetId; + })[0]; + let allPathLinks = []; + if (direction == "source") { + allPathLinks = self.findAllPath(startNode, endNode, true); + } else if (direction == "target") { + allPathLinks = self.findAllPath(endNode, startNode, true); + } else { + allPathLinks = self.findAllPath(startNode, endNode, false); + } + if (allPathLinks.length > 0) { + allPathLinks.forEach((path) => { + path.forEach((link) => { + self.selectedEdge(link); + }); + }); + self.refresh(); + } + return allPathLinks; + }; + VisualGraph.prototype.selectedEdge = function (link) { + if (link) { + link.selected = true; + this.scene.addToSelected(link); + link.target.selected = true; + this.scene.addToSelected(link.target); + link.source.selected = true; + this.scene.addToSelected(link.source); + } + }; + VisualGraph.prototype.checkAllCycle = function () { + var self = this; + var lgDegreeNodes = self.nodes.filter((node) => { + return ( + (node.inLinks || []).length >= 1 && (node.outLinks || []).length >= 1 + ); + }); + if (!lgDegreeNodes) { + return null; + } + let allCyclePath = []; + var length = lgDegreeNodes.length; + for (let i = 0; i < length; i++) { + var startNode = lgDegreeNodes[i]; + var inLinks = startNode.inLinks; + inLinks.forEach((link) => { + var targetNode = link.source; + if (lgDegreeNodes.indexOf(targetNode) != -1) { + var allPathLinks = self.findAllPath(startNode, targetNode, true); + if (allPathLinks && allPathLinks.length > 0) { + allCyclePath = allCyclePath.concat(allPathLinks); + } + } + }); + } + let resultPath = []; + allCyclePath.forEach((paths) => { + paths.forEach((path) => { + if (resultPath.indexOf(path) == -1) { + resultPath.push(path); + } + }); + }); + resultPath.forEach((link) => { + self.selectedEdge(link); + }); + self.refresh(); + return resultPath; + }; + VisualGraph.prototype.findNodeCyclePath = function (node) { + var self = this; + if (!node) { + return null; + } + let allCyclePath = []; + var inLinks = node.inLinks || []; + inLinks.forEach((link) => { + var targetNode = link.source; + var allPathLinks = self.findAllPath(node, targetNode, true); + if (allPathLinks && allPathLinks.length > 0) { + allCyclePath = allCyclePath.concat(allPathLinks); + } + }); + let resultPath = []; + allCyclePath.forEach((paths) => { + paths.forEach((path) => { + if (resultPath.indexOf(path) == -1) { + resultPath.push(path); + } + }); + }); + resultPath.forEach((link) => { + self.selectedEdge(link); + }); + self.refresh(); + return resultPath; + }; + VisualGraph.prototype.findClusters = function () { + var _self = this; + var nodeList = []; + this.nodes.forEach(function (n) { + if ((n.outLinks || []).length > 0) { + n.selected = true; + _self.scene.addToSelected(n); + nodeList.push(n); + } + }); + this.refresh(); + return nodeList; + }; + VisualGraph.prototype.findNDegreeRelates = function (degree) { + var _self = this; + var nodeList = []; + this.nodes.forEach(function (n) { + var total = (n.inLinks || []).length + (n.outLinks || []).length; + if (total >= degree) { + n.selected = true; + _self.scene.addToSelected(n); + nodeList.push(n); + } + }); + this.refresh(); + return nodeList; + }; + VisualGraph.prototype.nLayerRelates = function (nodeLabel, layerNum) { + var _self = this; + var currentNode = _self.nodes.filter(function (n) { + return n.label == nodeLabel; + })[0]; + var middleNode = []; + var nowOutsideNode = {}; + var nodeList = []; + if (null != currentNode) { + currentNode.selected = true; + _self.scene.addToSelected(currentNode); + nodeList.push(currentNode); + recursive(currentNode, layerNum); + _self.refresh(); + } + function recursive(node, nLayer) { + if (nLayer > 1) { + if (layerNum == nLayer && middleNode.indexOf(node) == -1) { + middleNode.push(node); + } + var level = layerNum - nLayer; + var inLinks = node.inLinks || []; + var outLinks = node.outLinks || []; + inLinks.forEach(function (l) { + if (middleNode.indexOf(l.source) == -1) { + middleNode.push(l.source); + if (nowOutsideNode[level] == undefined) { + nowOutsideNode[level] = []; + } + nowOutsideNode[level].push(l.source); + } + }); + outLinks.forEach(function (l) { + if (middleNode.indexOf(l.target) == -1) { + middleNode.push(l.target); + if (nowOutsideNode[level] == undefined) { + nowOutsideNode[level] = []; + } + nowOutsideNode[level].push(l.target); + } + }); + if (nowOutsideNode[level] == undefined) { + return; + } + nowOutsideNode[level].forEach(function (l) { + recursive(l, nLayer - 1); + }); + } else if (nLayer == 1) { + var inLinks = node.inLinks || []; + var outLinks = node.outLinks || []; + inLinks.forEach(function (l) { + if (middleNode.indexOf(l.source) == -1) { + l.source.selected = true; + _self.scene.addToSelected(l.source); + nodeList.push(l.source); + } + }); + outLinks.forEach(function (l) { + if (middleNode.indexOf(l.target) == -1) { + l.target.selected = true; + _self.scene.addToSelected(l.target); + nodeList.push(l.target); + } + }); + } + } + return nodeList; + }; + VisualGraph.prototype.removeCurrentLink = function () { + this.scene.remove(this.currentLink); + this.currentLink = null; + this.refresh(); + }; + VisualGraph.prototype.filterNodes = function (type, condition, value) { + this.nodes.forEach(function (n) { + var degree = 0; + if (type == "degree") { + degree = (n.inLinks || []).length + (n.outLinks || []).length; + } else if (type == "outdegree") { + degree = (n.outLinks || []).length; + } else if (type == "indegree") { + degree = (n.inLinks || []).length; + } + if (condition == 1) { + if (degree > value) { + n.visible = true; + } else { + n.visible = false; + } + } else if (condition == 2) { + if (degree == value) { + n.visible = true; + } else { + n.visible = false; + } + } else if (condition == 3) { + if (degree < value) { + n.visible = true; + } else { + n.visible = false; + } + } + }); + this.links.forEach(function (l) { + if (l.source.visible == false || l.target.visible == false) { + l.visible = false; + } else { + l.visible = true; + } + }); + this.refresh(); + }; + VisualGraph.prototype.filterLinks = function (condition, value) { + this.links.forEach(function (l) { + if (l.source.visible == true && l.target.visible == true) { + if (condition == 1) { + if (l.weight > value) { + l.visible = true; + } else { + l.visible = false; + } + } else if (condition == 2) { + if (l.weight == value) { + l.visible = true; + } else { + l.visible = false; + } + } else if (condition == 3) { + if (l.weight < value) { + l.visible = true; + } else { + l.visible = false; + } + } + } else { + l.visible = false; + } + }); + this.refresh(); + }; + VisualGraph.prototype.nodeMapColorsByDegree = function (propType, colorArr) { + var self = this; + var degreeArr = {}; + self.nodes.forEach(function (n) { + n.outdegree = (n.outLinks || []).length; + n.indegree = (n.inLinks || []).length; + n.degree = n.outdegree + n.indegree; + if (propType == "degree") { + degreeArr[n.degree + ""] = 1; + } else if (propType == "outdegree") { + degreeArr[n.outdegree + ""] = 1; + } else { + degreeArr[n.indegree + ""] = 1; + } + }); + var degrees = []; + for (var d in degreeArr) { + degrees.push(d); + } + degrees.sort(function (d1, d2) { + return Number(d2) - Number(d1); + }); + var ctool = new DGraph.ColorUtils(); + var colors = ctool.generateGradientColor( + colorArr[0], + colorArr[1], + degrees.length, + ); + self.nodes.forEach(function (n) { + var position = 0; + if (propType == "degree") { + position = degrees.indexOf(n.degree + ""); + } else if (propType == "outdegree") { + position = degrees.indexOf(n.outdegree + ""); + } else { + position = degrees.indexOf(n.indegree + ""); + } + n.fillColor = colors[position].replace("rgb(", "").replace(")", ""); + }); + this.refresh(); + }; + VisualGraph.prototype.setLineDirected = function (isDirect) { + this.links.forEach(function (l) { + l.showArrow = isDirect; + }); + this.refresh(); + }; + VisualGraph.prototype.setLineDashed = function (isDashed) { + var self = this; + var dashed = [0]; + if (isDashed) { + dashed = [8, 5]; + } + this.links.forEach(function (l) { + l.lineDash = dashed; + }); + this.refresh(); + }; + VisualGraph.prototype.setNodeSize = function (nodeSize) { + nodeSize = Number(nodeSize) || 60; + this.config.node.size = nodeSize; + this.nodes.forEach(function (node) { + node.width = node.height = Math.round(nodeSize); + }); + this.refresh(); + }; + VisualGraph.prototype.setNodeColor = function (hexColor) { + var rgbColor = this.converHexToRGB(hexColor); + this.defaultNodeColor = rgbColor; + this.nodes.forEach(function (node) { + node.fillColor = rgbColor; + }); + this.refresh(); + }; + VisualGraph.prototype.covertSencePoint = function (event) { + return DGraph.util.getEventPosition(event); + }; + VisualGraph.prototype.getMouseDownPosition = function () { + var self = this; + return { x: self.scene.mouseDownX, y: self.scene.mouseDownY }; + }; + VisualGraph.prototype.getPagePosition = function (x, y) { + if (arguments.length != 2) { + return { pageX: 0, pageY: 0 }; + } + return this.stage.getPagePosition(x, y); + }; + VisualGraph.prototype.getMousePosition = function (event) { + var p = DGraph.util.getEventPosition(event); + return { x: p.x, y: p.y }; + }; + VisualGraph.prototype.addNodeForDrag = function (_node, callback) { + var self = this; + self.stage.removeEventListener("mousemove"); + self.stage.mousemove(function (event) { + var p = self.scene.toSceneEvent(event); + self.stage.removeEventListener("mousemove"); + _node.id = _node.id == null ? self.nodeIdIndex++ : _node.id; + _node.x = p.x; + _node.y = p.y; + var node = self.newNode(_node); + node.fixed = true; + self.nodes.push(node); + self.scene.add(node); + self.refresh(); + if (callback && typeof callback === "function") { + callback(node); + } + }); + }; + VisualGraph.prototype.showAll = function () { + this.nodes.forEach(function (n) { + if (!n.visible) { + n.visible = true; + } + }); + this.links.forEach(function (l) { + if (!l.visible) { + l.visible = true; + } + }); + this.refresh(); + }; + VisualGraph.prototype.filterChangeVisible = function ( + entityFalse, + linkFalse, + infoFilterParams, + ) { + var _self = this; + _self.showAll(); + if (entityFalse.length > 0) { + _self.nodes.forEach(function (n) { + if (n.type && entityFalse.indexOf(n.type) != -1) { + n.visible = false; + } + }); + } + if (linkFalse.length > 0) { + _self.links.forEach(function (l) { + if (l.label && linkFalse.indexOf(l.label) != -1) { + l.visible = false; + } + }); + } + if (infoFilterParams["linkCount"]) { + var _compare = infoFilterParams["linkCount"][0]; + var _input = infoFilterParams["linkCount"][1]; + _self.nodes.forEach(function (n) { + var degree = (n.inLinks || []).length + (n.outLinks || []).length; + if (!showOrHide(_compare, _input, degree)) { + n.visible = false; + } + }); + } + if (infoFilterParams["linkOut"]) { + var _compare = infoFilterParams["linkOut"][0]; + var _input = infoFilterParams["linkOut"][1]; + _self.nodes.forEach(function (n) { + var degree = (n.outLinks || []).length; + if (!showOrHide(_compare, _input, degree)) { + n.visible = false; + } + }); + } + if (infoFilterParams["linkIn"]) { + var _compare = infoFilterParams["linkIn"][0]; + var _input = infoFilterParams["linkIn"][1]; + _self.nodes.forEach(function (n) { + var degree = (n.inLinks || []).length; + if (!showOrHide(_compare, _input, degree)) { + n.visible = false; + } + }); + } + if (infoFilterParams["linkWeight"]) { + var _compare = infoFilterParams["linkWeight"][0]; + var _input = infoFilterParams["linkWeight"][1]; + _self.links.forEach(function (l) { + if (!showOrHide(_compare, _input, l.weight)) { + l.visible = false; + } + }); + } + if (infoFilterParams["entityDesc"]) { + var _compare = infoFilterParams["entityDesc"][0]; + var _input = infoFilterParams["entityDesc"][1]; + _self.nodes.forEach(function (n) { + var nodeValue = n.label; + if (_compare == 1) { + if (nodeValue != _input) { + n.visible = false; + } + } else if (_compare == 2) { + if (nodeValue.indexOf(_input) == -1) { + n.visible = false; + } + } + }); + } + _self.links.forEach(function (l) { + if (!l.source.visible || !l.target.visible) { + l.visible = false; + } + }); + _self.refresh(); + function showOrHide(_compareType, compareValue, _degree) { + if (_compareType == 1) { + if (_degree > compareValue) { + return true; + } + return false; + } else if (_compareType == 2) { + if (_degree == compareValue) { + return true; + } + return false; + } else { + if (_degree < compareValue) { + return true; + } + return false; + } + return true; + } + }; + VisualGraph.prototype.delSelect = function () { + var _self = this; + var selectNodes = _self.nodes.filter((n) => { + return n.selected; + }); + selectNodes.forEach((n) => { + _self.removeOneNode(n); + }); + }; + VisualGraph.prototype.removeOneNode = function (node) { + var _self = this; + if (node) { + var links = []; + var index = _self.nodes.indexOf(node); + if (index > -1) { + (node.inLinks || []).forEach(function (l) { + links.push(l); + }); + (node.outLinks || []).forEach(function (l) { + links.push(l); + }); + _self.scene.remove(node); + _self.nodes.splice(index, 1); + } + links.forEach(function (l) { + index = _self.links.indexOf(l); + if (index > -1) { + _self.scene.remove(l); + _self.links.splice(index, 1); + } + }); + node = null; + _self.refresh(); + } + }; + VisualGraph.prototype.clearAll = function () { + if (this.scene) { + this.scene.clear(); + } + this.nodes = []; + this.links = []; + this.nodeIdIndex = 1; + this.currentNode = null; + this.currentLink = null; + this.currentLayout = null; + this.clusterGroups = []; + this.currentCluster = null; + this.drawLinkFlag = false; + this.virLink = null; + this.setZoom("zoom1"); + this.stopRunningLayout(10); + this.refresh(); + }; + VisualGraph.prototype.destroy = function () { + this.clearAll(); + this.scene.removeAllEventListener(); + this.stage.removeAllEventListener(); + }; + VisualGraph.prototype.rightMenuOprate = function (optType) { + var self = this; + switch (optType) { + case "allSelect": + self.selectAll(); + break; + case "rebackSel": + self.reverseSelect(); + break; + case "showAll": + self.showAll(); + break; + case "selRelate": + self.selectRelate(); + break; + case "showNodes": + self.showNodes(); + break; + case "showSelNode": + self.showSelected(); + break; + case "hideSelNode": + self.hideSelected(); + break; + case "delSelect": + self.delSelect(); + break; + case "clearAll": + this.clearAll(); + break; + case "hideIsolatedNodes": + self.hideIsolatedNodes(); + break; + case "showLinks": + self.showAllLink(); + break; + case "hideLinks": + self.hideAllLink(); + break; + case "sourcelphaMap": + self.nodeMapAlphaByDegree(); + break; + case "saveImage": + self.saveImage(); + break; + case "deleteNode": + self.deleteNode(self.currentNode); + self.currentNode = null; + break; + case "nodeConnent": + self.beginAddLine(); + break; + case "delEdge": + self.deleteLink(self.currentLink); + self.currentLink = null; + break; + case "expanded": + self.expanded(self.currentNode); + break; + case "contract": + self.contract(self.currentNode); + break; + case "directedLine": + self.setLineDirected(true); + break; + case "undirectedLine": + self.setLineDirected(false); + break; + case "showLineLabel": + self.showLinkLabel(true); + break; + case "hideLineLabel": + self.showLinkLabel(false); + break; + case "Rline": + self.setLineDashed(false); + break; + case "Vline": + self.setLineDashed(true); + break; + default: + break; + } + }; + VisualGraph.prototype.beginAddLine = function (callback) { + this.drawLinkFlag = true; + if (callback && typeof callback === "function") { + this.drawLineCallback = callback; + } + }; + VisualGraph.prototype.drawVirtualLine = function (nodeA, nodeB) { + var self = this; + var line = self.newEdge(nodeA, nodeB); + self.scene.add(line); + return line; + }; + VisualGraph.prototype.drawVirtualNode = function (config) { + var self = this; + var node = self.newNode(config || { x: 10, y: 10, width: 5 }); + node.dragable = false; + node.fixed = true; + self.scene.add(node); + return node; + }; + VisualGraph.prototype.removeLine = function (line) { + this.scene.remove(line); + this.refresh(); + }; + VisualGraph.prototype.computeParentAngle = function (node) { + node = node || {}; + var angleRadian = 0; + var parentNodes = [], + parentFlag = true; + (node.inLinks || []).forEach(function (l) { + parentNodes.push(l.source); + }); + if (parentNodes.length == 0) { + parentFlag = false; + (node.outLinks || []).forEach(function (l) { + parentNodes.push(l.target); + }); + } + var maxParentNode; + maxParentNode = parentNodes.sort(function (n1, n2) { + if (parentFlag) { + return ( + (n1.outLinks || []).length + (n1.inLinks || []).length < + (n2.outLinks || []).length + (n2.inLinks || []).length + ); + } else { + return ( + (n1.outLinks || []).length + (n1.inLinks || []).length > + (n2.outLinks || []).length + (n2.inLinks || []).length + ); + } + })[0]; + if (maxParentNode) { + var xp = maxParentNode.cx, + yp = maxParentNode.cy; + var x0 = node.cx, + y0 = node.cy; + angleRadian = Math.atan2(y0 - yp, x0 - xp); + } + return angleRadian; + }; + VisualGraph.prototype.activeAddNodeLinks = function (_nodes, _links) { + var _self = this; + _self.incremaNodesCodinate(_nodes); + var newNodes = [], + newLinks = []; + (_nodes || []).forEach(function (_node, i) { + var hasNodes = _self.nodes.filter(function (n) { + return n.id == _node.id; + }); + if (hasNodes.length == 0) { + if (_self.currentNode != null) { + if (_node.id != _self.currentNode.id) { + var node = _self.newNode(_node); + _self.nodes.push(node); + _self.scene.add(node); + newNodes.push(node); + } else { + _self.currentNode.id = _node.id; + } + } else { + var node = _self.newNode(_node); + _self.nodes.push(node); + _self.scene.add(node); + newNodes.push(node); + } + } + }); + var _nodes_ = _self.nodes; + (_links || []).forEach(function (_link) { + var hasLinks = _self.links.filter(function (l) { + return ( + l.id == _link.id || + (l.source.id == _link.source && l.target.id == _link.target) + ); + }); + if (hasLinks.length == 0) { + var sourceNode = _nodes_.filter(function (_node) { + return _node.id == _link.source; + })[0]; + var targetNode = _nodes_.filter(function (_node) { + return _node.id == _link.target; + })[0]; + if (sourceNode != null && targetNode != null) { + var link = _self.newEdge(sourceNode, targetNode); + link.id = _link.id || ""; + link.type = _link.type || "default"; + link.label = _link.label || ""; + link.showlabel = _link.showlabel || _self.config.link.label.show; + link.lineWidth = + Number(_link.lineWidth) || _self.config.link.lineWidth; + link.weight = Number(_link.weight) || 1; + link.lineType = _link.lineType || _self.config.link.lineType; + link.lineDash = _link.lineDash || _self.config.link.lineDash; + link.strokeColor = _link.color || _self.config.link.color; + link.font = _link.font || _self.config.link.label.font; + link.fontColor = _link.fontColor || _self.config.link.label.color; + link.properties = _link.properties || {}; + _self.links.push(link); + _self.scene.add(link); + newLinks.push(link); + } + } + }); + var unResetNodes = _self.nodes.filter(function (n) { + return !n.hasOwnProperty("charge"); + }); + unResetNodes.forEach(function (n) { + newNodes.push(n); + }); + if (_self.currentNode != null) { + newNodes.push(_self.currentNode); + } + if (newNodes.length > 1) { + if (_self.currentNode != null) { + (_self.currentNode.inLinks || []).forEach(function (l) { + var isNewNode = newNodes.filter(function (n) { + return n == l.source; + }); + if (isNewNode == null || isNewNode.length == 0) { + l.source.fixed = true; + } + }); + _self.currentNode.fixed = true; + } + _self.collideNodes(_self.nodes); + var centerX = (_self.currentNode || { x: 100 }).x || 100; + var centerY = (_self.currentNode || { y: 100 }).y || 100; + newNodes.forEach((tnode) => { + var targetX = tnode.x, + targetY = tnode.y; + (tnode.x = centerX), (tnode.y = centerY); + _self.animate({ + targets: tnode, + x: targetX, + y: targetY, + duration: 500, + easing: "spring", + }); + }); + } + }; + VisualGraph.prototype.incremaNodesCodinate = function (nodes) { + var radius = computeRadius(nodes); + var centerNode = this.currentNode || { x: 100, y: 100 }; + var angle = this.computeParentAngle(centerNode); + var x1 = centerNode.x + radius * Math.cos(angle); + var y1 = centerNode.y + radius * Math.sin(angle); + centerNode.x = x1; + centerNode.y = y1; + var nodeCount = nodes.length; + var xyArr = getXY(centerNode, nodeCount, radius); + nodes.forEach((n, i) => { + n.x = xyArr[i].x; + n.y = xyArr[i].y; + }); + function computeRadius(_nodes) { + var nodeRadius = 20; + var area = 2 * Math.PI * nodeRadius * nodeRadius * _nodes.length; + var radius = Math.round(Math.sqrt(area, 3)); + return radius; + } + function getXYA(centerNode, nodeCount, raduis) { + var aop = 360.0 / nodeCount; + var arr = []; + for (var i = 0; i < nodeCount; i++) { + var r1 = raduis; + if (nodeCount > 10) { + r1 = i % 2 == 0 ? raduis + 35 : raduis - 35; + } + var ao = i * aop; + var o1 = {}; + o1.x = centerNode.x + r1 * Math.cos((ao * Math.PI) / 360); + o1.y = centerNode.y + r1 * Math.sin((ao * Math.PI) / 360); + arr[i] = o1; + } + return arr; + } + function getXY(centerNode, nodeCount, raduis) { + if (nodeCount <= 30) { + return getXYA(centerNode, nodeCount, raduis); + } + var arr = []; + for (var i = 0; i < nodeCount; i++) { + var phi = Math.acos(-1 + (2 * i) / nodeCount); + var theta = Math.sqrt(nodeCount * Math.PI) * phi; + var sinPhiRadius = Math.sin(phi) * radius; + var o1 = {}; + o1.x = centerNode.x + sinPhiRadius * Math.sin(theta); + o1.y = centerNode.y + Math.cos(phi) * radius; + arr[i] = o1; + } + return arr; + } + }; + VisualGraph.prototype.collideNodes = function (nodes) { + var self = this; + var q = self.quadtree(nodes); + let num = 1; + while (num++ <= 20) { + nodes.forEach(function (_node) { + q.visit(collide(_node)); + }); + } + function collide(node) { + var nr = node.radius * node.scaleX * 2, + nx1 = node.x - nr, + nx2 = node.x + nr, + ny1 = node.y - nr, + ny2 = node.y + nr; + return function (quad, x1, y1, x2, y2) { + if (quad.point && quad.point !== node) { + var x = node.x - quad.point.x, + y = node.y - quad.point.y, + l = Math.sqrt(x * x + y * y), + r = nr + quad.point.radius; + if (l < r) { + l = ((l - r) / l) * 0.5; + node.x -= x *= l; + node.y -= y *= l; + quad.point.x += x; + quad.point.y += y; + } + } + return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; + }; + } + }; + VisualGraph.prototype.quadtree = function (points) { + var p, + i = -1, + n = points.length; + if (n && isNaN(points[0].x)) { + points = points.map(function (p) { + return { x: p[0], y: p[1] }; + }); + } + var x1 = (y1 = Infinity); + var x2 = (y2 = -Infinity); + while (++i < n) { + p = points[i]; + if (p.x < x1) { + x1 = p.x; + } + if (p.y < y1) { + y1 = p.y; + } + if (p.x > x2) { + x2 = p.x; + } + if (p.y > y2) { + y2 = p.y; + } + } + var dx = x2 - x1, + dy = y2 - y1; + if (dx > dy) { + y2 = y1 + dx; + } else { + x2 = x1 + dy; + } + function insert(n, p, x1, y1, x2, y2) { + if (isNaN(p.x) || isNaN(p.y)) { + return; + } + if (n.leaf) { + var v = n.point; + if (v) { + if (Math.abs(v.x - p.x) + Math.abs(v.y - p.y) < 0.01) { + insertChild(n, p, x1, y1, x2, y2); + } else { + n.point = null; + insertChild(n, v, x1, y1, x2, y2); + insertChild(n, p, x1, y1, x2, y2); + } + } else { + n.point = p; + } + } else { + insertChild(n, p, x1, y1, x2, y2); + } + } + function insertChild(n, p, x1, y1, x2, y2) { + var sx = (x1 + x2) * 0.5, + sy = (y1 + y2) * 0.5, + right = p.x >= sx, + bottom = p.y >= sy, + i = (bottom << 1) + right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = { leaf: true, nodes: [], point: null }); + if (right) { + x1 = sx; + } else { + x2 = sx; + } + if (bottom) { + y1 = sy; + } else { + y2 = sy; + } + insert(n, p, x1, y1, x2, y2); + } + function quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * 0.5, + sy = (y1 + y2) * 0.5, + children = node.nodes; + if (children[0]) { + quadtreeVisit(f, children[0], x1, y1, sx, sy); + } + if (children[1]) { + quadtreeVisit(f, children[1], sx, y1, x2, sy); + } + if (children[2]) { + quadtreeVisit(f, children[2], x1, sy, sx, y2); + } + if (children[3]) { + quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + } + var root = { leaf: true, nodes: [], point: null }; + root.add = function (p) { + insert(root, p, x1, y1, x2, y2); + }; + root.visit = function (f) { + quadtreeVisit(f, root, x1, y1, x2, y2); + }; + points.forEach(root.add); + return root; + }; + VisualGraph.prototype.stopRunningLayout = function (layzeTime) { + var self = this; + setTimeout(function () { + cancelAnimationFrame(self.forceOptions.loopName); + self.forceOptions.loopName = null; + self.forceOptions.alpha = 0; + }, layzeTime); + }; + VisualGraph.prototype.loopRunLayout = function (callback) { + var _self = this; + if (typeof callback == "function") { + cancelAnimationFrame(_self.forceOptions.loopName); + _self.forceOptions.loopName = null; + function loop() { + cancelAnimationFrame(_self.forceOptions.loopName); + callback(); + _self.refresh(); + _self.forceOptions.loopName = requestAnimationFrame(loop); + } + _self.forceOptions.loopName = requestAnimationFrame(loop); + } + }; + VisualGraph.prototype.runLayoutEngin = function (nodes, links, alpha) { + var _self = this; + if (nodes.length == 0) { + return; + } + _self.forceOptions.alpha = alpha || 0.5; + _self.loopRunLayout(tick); + function tick() { + if ((_self.forceOptions.alpha *= 0.99) < 0.001) { + _self.stopRunningLayout(10); + _self.forceOptions.alpha = 0; + return; + } + var q, i, o, s, t, l, k, x, y; + links.forEach(function (link, i) { + s = link.source; + t = link.target; + x = t.x - s.x; + y = t.y - s.y; + if ((l = x * x + y * y)) { + l = + (_self.forceOptions.alpha * + link.strength * + ((l = Math.sqrt(l)) - link.distance)) / + l; + x *= l; + y *= l; + t.x -= x * (k = s.weight / (t.weight + s.weight)); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + }); + var n = nodes.length; + if ((k = _self.forceOptions.alpha * _self.forceOptions.gravity)) { + x = _self.forceOptions.size[0] / 2; + y = _self.forceOptions.size[1] / 2; + i = -1; + if (k) { + while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + } + if (_self.forceOptions.charge) { + forceAccumulate((q = _self.quadtree(nodes)), _self.forceOptions.alpha); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + if (!o.isDragging) { + o.x = o.px; + o.y = o.py; + } else { + o.px = o.x; + o.py = o.y; + } + } else { + o.x -= (o.px - (o.px = o.x)) * _self.forceOptions.friction; + o.y -= (o.py - (o.py = o.y)) * _self.forceOptions.friction; + if (_self.forceOptions.noverlap == true) { + q.visit(collide(o)); + } + } + } + function repulse(node) { + return function (quad, x1, y1, x2, y2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, + dy = quad.cy - node.y, + dn = 1 / Math.sqrt(dx * dx + dy * dy); + if ((x2 - x1) * dn < _self.forceOptions.theta) { + var k = quad.charge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + return true; + } + if (quad.point && isFinite(dn)) { + var k = quad.pointCharge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + function collide(node) { + var nodeR = node.radius * node.scaleX * 2, + nx1 = node.x - nodeR, + nx2 = node.x + nodeR, + ny1 = node.y - nodeR, + ny2 = node.y + nodeR; + return function (quad, x1, y1, x2, y2) { + if (quad.point && quad.point !== node) { + var x = node.x - quad.point.x, + y = node.y - quad.point.y, + l = Math.sqrt(x * x + y * y), + r = nodeR + quad.point.radius; + if (l < r) { + l = ((l - r) / l) * 0.5; + node.x -= x *= l; + node.y -= y *= l; + quad.point.x += x; + quad.point.y += y; + } + } + return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1; + }; + } + } + function forceAccumulate(quad, alpha) { + var cx = 0, + cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, + n = nodes.length, + i = -1, + c; + while (++i < n) { + c = nodes[i]; + if (c == null) { + continue; + } + forceAccumulate(c, alpha); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - 0.5; + quad.point.y += Math.random() - 0.5; + } + var k = _self.forceOptions.alpha * quad.point.charge; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + }; + VisualGraph.prototype.getAllSelectedNodes = function () { + var nodes = this.nodes.filter(function (n) { + return n.selected == true; + }); + return nodes; + }; + VisualGraph.prototype.findNodeByAttr = function (attr, value) { + var nodes = this.nodes.filter(function (n) { + return n[attr] == value; + }); + if (nodes.length > 0) { + var node = nodes[0]; + return node; + } + return null; + }; + VisualGraph.prototype.findNodeById = function (nodeId) { + var nodes = this.nodes.filter(function (n) { + return n.id == nodeId; + }); + if (nodes.length > 0) { + var node = nodes[0]; + node.selected = true; + node.visible = true; + this.scene.addToSelected(node); + this.focusTargetEle(node); + return node; + } + return null; + }; + VisualGraph.prototype.updateNodeLabel = function (nodeId, nodeName) { + var node = this.nodes.filter(function (_node) { + return _node.id == nodeId; + })[0]; + if (node) { + node.label = nodeName; + node.text = nodeName; + this.refresh(); + } + }; + VisualGraph.prototype.switchAnimate = function (flag) { + if (!flag) { + this.stage.fps = -50; + } else { + this.stage.fps = 50; + } + }; + VisualGraph.prototype.setStaticMode = function (flag) { + this.stage.staticMode = flag; + }; + VisualGraph.prototype.setSmoothWheelMode = function (flag) { + this.stage.smoothWheelMode = flag; + }; + VisualGraph.prototype.setShowDetailScale = function (scale) { + this.stage.showDetailScale = Number(scale) || 0.5; + }; + VisualGraph.prototype.setDragHideLineEffect = function (flag) { + this.stage.openDragHideEffect = Boolean(flag); + }; + VisualGraph.prototype.updateThumbnail = function () { + this.stage.thumbnail.visible && this.stage.thumbnail.update(); + }; + VisualGraph.prototype.randomColor = function () { + return ( + Math.floor(255 * Math.random()) + + "," + + Math.floor(360 * Math.random()) + + "," + + Math.floor(255 * Math.random()) + ); + }; + VisualGraph.prototype.buildVisualGraphData = function (_nodes, _links) { + var nodeIdMapNode = new Map(); + _nodes.forEach(function (n) { + n.scaleX = 1; + n.scaleY = 1; + n.radius = n.radius || n.width || 30; + n.x = n.x || Math.round(Math.random() * 1000); + n.y = n.y || Math.round(Math.random() * 1000); + n.cx = n.x; + n.cy = n.x; + if (n.id == 1) { + n.selected = true; + } else { + n.selected = false; + } + nodeIdMapNode.set(n.id, n); + }); + var links = []; + _links.forEach(function (l) { + var sourceNode = nodeIdMapNode.get(l.source), + targetNode = nodeIdMapNode.get(l.target); + if (sourceNode) { + sourceNode.outLinks = sourceNode.outLinks || []; + sourceNode.outLinks.push({ source: sourceNode, target: targetNode }); + } + if (targetNode) { + targetNode.inLinks = targetNode.inLinks || []; + targetNode.inLinks.push({ source: sourceNode, target: targetNode }); + } + links.push({ source: sourceNode, target: targetNode }); + }); + var newNodes = []; + nodeIdMapNode.forEach(function (value, key) { + newNodes.push(value); + }); + return { nodes: newNodes, links: links }; + }; + VisualGraph.prototype.algorithm = function () { + return DGraph.util; + }; + VisualGraph.prototype.preResetNodeCoords = function ( + nodes, + center = [100, 100], + nodeSpace = 80, + needScale = true, + ) { + var newNodes = [].concat(nodes); + newNodes.sort((nodeA, nodeB) => { + return ( + (nodeB.inLinks || []).length + + (nodeB.outLinks || []).length - + ((nodeA.inLinks || []).length + (nodeA.outLinks || []).length) + ); + }); + var theta = Math.PI * (3 - Math.sqrt(5)); + var centerX = center[0]; + var centerY = center[1]; + var radius = 0, + angle = 0; + newNodes.forEach((node, i) => { + radius = nodeSpace * Math.sqrt((i += 0.5)); + angle = theta * i; + if (i == 0) { + node = centerX; + node = centerY; + node.fixed = true; + } else { + node.x = centerX + radius * Math.cos(angle); + node.y = centerY + radius * Math.sin(angle); + } + }); + if (needScale) { + this.moveCenter(this.scene.scaleRange[0]); + } + }; + VisualGraph.prototype.showLoadProcess = function ( + loadText = "Loading", + percent = 0.1, + ) { + this.stage.showLoading(loadText, percent); + }; + VisualGraph.prototype.hideLoadProcess = function () { + this.stage.hideLoading(); + }; + VisualGraph.prototype.getViewCenter = function () { + return { + x: this.stage.width / 2 - this.scene.translateX, + y: this.stage.height / 2 - this.scene.translateY, + }; + }; + VisualGraph.prototype.genRgbGradientColors = function ( + num, + rbgColorFrom, + rgbColorTo, + ) { + var ctool = new DGraph.ColorUtils(); + var colors = ctool.genRgbGradientColors(num, rbgColorFrom, rgbColorTo); + return colors; + }; + VisualGraph.prototype.setDataDragAble = function (dragable = true) { + this.scene.dragable = dragable; + }; + VisualGraph.prototype.animate = function (config) { + var self = this; + var defaultInstanceSettings = { + update: null, + begin: null, + loopBegin: null, + changeBegin: null, + change: null, + changeComplete: null, + loopComplete: null, + complete: null, + loop: 1, + direction: "normal", + autoplay: true, + timelineOffset: 0, + }; + var defaultTweenSettings = { + duration: 1000, + delay: 0, + endDelay: 0, + easing: "easeOutElastic(1,.5)", + round: 0, + }; + function minMax(val, min, max) { + return Math.min(Math.max(val, min), max); + } + function applyArguments(func, args) { + if (func) { + return func.apply(null, args); + } + return; + } + var is = { + arr: function (a) { + return Array.isArray(a); + }, + obj: function (a) { + return Object.prototype.toString.call(a).indexOf("Object") > -1; + }, + str: function (a) { + return typeof a === "string"; + }, + fnc: function (a) { + return typeof a === "function"; + }, + und: function (a) { + return typeof a === "undefined"; + }, + key: function (a) { + return ( + !defaultInstanceSettings.hasOwnProperty(a) && + !defaultTweenSettings.hasOwnProperty(a) && + a !== "targets" && + a !== "keyframes" + ); + }, + }; + function parseEasingParameters(string) { + var match = /\(([^)]+)\)/.exec(string); + return match + ? match[1].split(",").map(function (p) { + return parseFloat(p); + }) + : []; + } + var cache = { springs: {} }; + function spring(string, duration) { + var params = parseEasingParameters(string); + var mass = minMax(is.und(params[0]) ? 1 : params[0], 0.1, 100); + var stiffness = minMax(is.und(params[1]) ? 100 : params[1], 0.1, 100); + var damping = minMax(is.und(params[2]) ? 10 : params[2], 0.1, 100); + var velocity = minMax(is.und(params[3]) ? 0 : params[3], 0.1, 100); + var w0 = Math.sqrt(stiffness / mass); + var zeta = damping / (2 * Math.sqrt(stiffness * mass)); + var wd = zeta < 1 ? w0 * Math.sqrt(1 - zeta * zeta) : 0; + var a = 1; + var b = zeta < 1 ? (zeta * w0 + -velocity) / wd : -velocity + w0; + function solver(t) { + var progress = duration ? (duration * t) / 1000 : t; + if (zeta < 1) { + progress = + Math.exp(-progress * zeta * w0) * + (a * Math.cos(wd * progress) + b * Math.sin(wd * progress)); + } else { + progress = (a + b * progress) * Math.exp(-progress * w0); + } + if (t === 0 || t === 1) { + return t; + } + return 1 - progress; + } + function getDuration() { + var cached = cache.springs[string]; + if (cached) { + return cached; + } + var frame = 1 / 6; + var elapsed = 0; + var rest = 0; + while (true) { + elapsed += frame; + if (solver(elapsed) === 1) { + rest++; + if (rest >= 16) { + break; + } + } else { + rest = 0; + } + } + var duration = elapsed * frame * 1000; + cache.springs[string] = duration; + return duration; + } + return duration ? solver : getDuration; + } + function steps(steps) { + if (steps === void 0) steps = 10; + return function (t) { + return Math.round(t * steps) * (1 / steps); + }; + } + var penner = (function () { + var eases = { + linear: function () { + return function (t) { + return t; + }; + }, + }; + var functionEasings = { + Sine: function () { + return function (t) { + return 1 - Math.cos((t * Math.PI) / 2); + }; + }, + Circ: function () { + return function (t) { + return 1 - Math.sqrt(1 - t * t); + }; + }, + Back: function () { + return function (t) { + return t * t * (3 * t - 2); + }; + }, + Bounce: function () { + return function (t) { + var pow2, + b = 4; + while (t < ((pow2 = Math.pow(2, --b)) - 1) / 11) {} + return ( + 1 / Math.pow(4, 3 - b) - + 7.5625 * Math.pow((pow2 * 3 - 2) / 22 - t, 2) + ); + }; + }, + Elastic: function (amplitude, period) { + if (amplitude === void 0) amplitude = 1; + if (period === void 0) period = 0.5; + var a = minMax(amplitude, 1, 10); + var p = minMax(period, 0.1, 2); + return function (t) { + return t === 0 || t === 1 + ? t + : -a * + Math.pow(2, 10 * (t - 1)) * + Math.sin( + ((t - 1 - (p / (Math.PI * 2)) * Math.asin(1 / a)) * + (Math.PI * 2)) / + p, + ); + }; + }, + }; + var baseEasings = ["Quad", "Cubic", "Quart", "Quint", "Expo"]; + baseEasings.forEach(function (name, i) { + functionEasings[name] = function () { + return function (t) { + return Math.pow(t, i + 2); + }; + }; + }); + Object.keys(functionEasings).forEach(function (name) { + var easeIn = functionEasings[name]; + eases["easeIn" + name] = easeIn; + eases["easeOut" + name] = function (a, b) { + return function (t) { + return 1 - easeIn(a, b)(1 - t); + }; + }; + eases["easeInOut" + name] = function (a, b) { + return function (t) { + return t < 0.5 + ? easeIn(a, b)(t * 2) / 2 + : 1 - easeIn(a, b)(t * -2 + 2) / 2; + }; + }; + }); + return eases; + })(); + function parseEasings(easing, duration) { + if (is.fnc(easing)) { + return easing; + } + var name = easing.split("(")[0]; + var ease = penner[name]; + var args = parseEasingParameters(easing); + switch (name) { + case "spring": + return spring(easing, duration); + case "steps": + return applyArguments(steps, args); + default: + return applyArguments(ease, args); + } + } + function filterArray(arr, callback) { + var len = arr.length; + var thisArg = arguments.length >= 2 ? arguments[1] : void 0; + var result = []; + for (var i = 0; i < len; i++) { + if (i in arr) { + var val = arr[i]; + if (callback.call(thisArg, val, i, arr)) { + result.push(val); + } + } + } + return result; + } + function flattenArray(arr) { + return arr.reduce(function (a, b) { + return a.concat(is.arr(b) ? flattenArray(b) : b); + }, []); + } + function toArray(o) { + if (is.arr(o)) { + return o; + } + if (o instanceof NodeList || o instanceof HTMLCollection) { + return [].slice.call(o); + } + return [o]; + } + function cloneObject(o) { + var clone = {}; + for (var p in o) { + clone[p] = o[p]; + } + return clone; + } + function replaceObjectProps(o1, o2) { + var o = cloneObject(o1); + for (var p in o1) { + o[p] = o2.hasOwnProperty(p) ? o2[p] : o1[p]; + } + return o; + } + function mergeObjects(o1, o2) { + var o = cloneObject(o1); + for (var p in o2) { + o[p] = is.und(o1[p]) ? o2[p] : o1[p]; + } + return o; + } + function getUnit(val) { + var split = + /[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?(%|px|pt|em|rem|in|cm|mm|ex|ch|pc|vw|vh|vmin|vmax|deg|rad|turn)?$/.exec( + val, + ); + if (split) { + return split[1]; + } + } + function getFunctionValue(val, animatable) { + if (!is.fnc(val)) { + return val; + } + return val(animatable.target, animatable.id, animatable.total); + } + function getOriginalTargetValue(target, propName, unit, animatable) { + return target[propName] || 0; + } + function getRelativeValue(to, from) { + var operator = /^(\*=|\+=|-=)/.exec(to); + if (!operator) { + return to; + } + var u = getUnit(to) || 0; + var x = parseFloat(from); + var y = parseFloat(to.replace(operator[0], "")); + switch (operator[0][0]) { + case "+": + return x + y + u; + case "-": + return x - y + u; + case "*": + return x * y + u; + } + } + function validateValue(val, unit) { + if (/\s/g.test(val)) { + return val; + } + var originalUnit = getUnit(val); + var unitLess = originalUnit + ? val.substr(0, val.length - originalUnit.length) + : val; + if (unit) { + return unitLess + unit; + } + return unitLess; + } + function decomposeValue(val, unit) { + var rgx = /[+-]?\d*\.?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g; + var value = validateValue(val, unit) + ""; + return { + original: value, + numbers: value.match(rgx) ? value.match(rgx).map(Number) : [0], + strings: is.str(val) || unit ? value.split(rgx) : [], + }; + } + function parseTargets(targets) { + var targetsArray = targets + ? flattenArray( + is.arr(targets) ? targets.map(toArray) : toArray(targets), + ) + : []; + return filterArray(targetsArray, function (item, pos, self) { + return self.indexOf(item) === pos; + }); + } + function getAnimatables(targets) { + var parsed = parseTargets(targets); + return parsed.map(function (t, i) { + return { target: t, id: i, total: parsed.length }; + }); + } + function normalizePropertyTweens(prop, tweenSettings) { + var settings = cloneObject(tweenSettings); + if (/^spring/.test(settings.easing)) { + settings.duration = spring(settings.easing); + } + if (is.arr(prop)) { + var l = prop.length; + var isFromTo = l === 2 && !is.obj(prop[0]); + if (!isFromTo) { + if (!is.fnc(tweenSettings.duration)) { + settings.duration = tweenSettings.duration / l; + } + } else { + prop = { value: prop }; + } + } + var propArray = is.arr(prop) ? prop : [prop]; + return propArray + .map(function (v, i) { + var obj = is.obj(v) ? v : { value: v }; + if (is.und(obj.delay)) { + obj.delay = !i ? tweenSettings.delay : 0; + } + if (is.und(obj.endDelay)) { + obj.endDelay = + i === propArray.length - 1 ? tweenSettings.endDelay : 0; + } + return obj; + }) + .map(function (k) { + return mergeObjects(k, settings); + }); + } + function flattenKeyframes(keyframes) { + var propertyNames = filterArray( + flattenArray( + keyframes.map(function (key) { + return Object.keys(key); + }), + ), + function (p) { + return is.key(p); + }, + ).reduce(function (a, b) { + if (a.indexOf(b) < 0) { + a.push(b); + } + return a; + }, []); + var properties = {}; + var loop = function (i) { + var propName = propertyNames[i]; + properties[propName] = keyframes.map(function (key) { + var newKey = {}; + for (var p in key) { + if (is.key(p)) { + if (p == propName) { + newKey.value = key[p]; + } + } else { + newKey[p] = key[p]; + } + } + return newKey; + }); + }; + for (var i = 0; i < propertyNames.length; i++) loop(i); + } + function getProperties(tweenSettings, params) { + var properties = []; + var keyframes = params.keyframes; + if (keyframes) { + params = mergeObjects(flattenKeyframes(keyframes), params); + } + for (var p in params) { + if (is.key(p)) { + properties.push({ + name: p, + tweens: normalizePropertyTweens(params[p], tweenSettings), + }); + } + } + return properties; + } + function normalizeTweenValues(tween, animatable) { + var t = {}; + for (var p in tween) { + var value = getFunctionValue(tween[p], animatable); + if (is.arr(value)) { + value = value.map(function (v) { + return getFunctionValue(v, animatable); + }); + if (value.length === 1) { + value = value[0]; + } + } + t[p] = value; + } + t.duration = parseFloat(t.duration); + t.delay = parseFloat(t.delay); + return t; + } + function normalizeTweens(prop, animatable) { + var previousTween; + return prop.tweens.map(function (t) { + var tween = normalizeTweenValues(t, animatable); + var tweenValue = tween.value; + var to = is.arr(tweenValue) ? tweenValue[1] : tweenValue; + var toUnit = getUnit(to); + var originalValue = getOriginalTargetValue( + animatable.target, + prop.name, + toUnit, + animatable, + ); + var previousValue = previousTween + ? previousTween.to.original + : originalValue; + var from = is.arr(tweenValue) ? tweenValue[0] : previousValue; + var fromUnit = getUnit(from) || getUnit(originalValue); + var unit = toUnit || fromUnit; + if (is.und(to)) { + to = previousValue; + } + tween.from = decomposeValue(from, unit); + tween.to = decomposeValue(getRelativeValue(to, from), unit); + tween.start = previousTween ? previousTween.end : 0; + tween.end = tween.start + tween.delay + tween.duration + tween.endDelay; + tween.easing = parseEasings(tween.easing, tween.duration); + previousTween = tween; + return tween; + }); + } + var setProgressValue = { + object: function (t, p, v) { + return (t[p] = v); + }, + }; + function setTargetsValue(targets, properties) { + var animatables = getAnimatables(targets); + animatables.forEach(function (animatable) { + for (var property in properties) { + var value = getFunctionValue(properties[property], animatable); + var target = animatable.target; + var valueUnit = getUnit(value); + var originalValue = getOriginalTargetValue( + target, + property, + valueUnit, + animatable, + ); + var unit = valueUnit || getUnit(originalValue); + var to = getRelativeValue(validateValue(value, unit), originalValue); + var animType = "object"; + setProgressValue[animType]( + target, + property, + to, + animatable.transforms, + true, + ); + } + }); + } + function createAnimation(animatable, prop) { + var tweens = normalizeTweens(prop, animatable); + var lastTween = tweens[tweens.length - 1]; + return { + type: "object", + property: prop.name, + animatable: animatable, + tweens: tweens, + duration: lastTween.end, + delay: tweens[0].delay, + endDelay: lastTween.endDelay, + }; + } + function getAnimations(animatables, properties) { + return filterArray( + flattenArray( + animatables.map(function (animatable) { + return properties.map(function (prop) { + return createAnimation(animatable, prop); + }); + }), + ), + function (a) { + return !is.und(a); + }, + ); + } + function getInstanceTimings(animations, tweenSettings) { + var animLength = animations.length; + var getTlOffset = function (anim) { + return anim.timelineOffset ? anim.timelineOffset : 0; + }; + var timings = {}; + timings.duration = animLength + ? Math.max.apply( + Math, + animations.map(function (anim) { + return getTlOffset(anim) + anim.duration; + }), + ) + : tweenSettings.duration; + timings.delay = animLength + ? Math.min.apply( + Math, + animations.map(function (anim) { + return getTlOffset(anim) + anim.delay; + }), + ) + : tweenSettings.delay; + timings.endDelay = animLength + ? timings.duration - + Math.max.apply( + Math, + animations.map(function (anim) { + return getTlOffset(anim) + anim.duration - anim.endDelay; + }), + ) + : tweenSettings.endDelay; + return timings; + } + var instanceID = 0; + function createNewInstance(params) { + var instanceSettings = replaceObjectProps( + defaultInstanceSettings, + params, + ); + var tweenSettings = replaceObjectProps(defaultTweenSettings, params); + var properties = getProperties(tweenSettings, params); + var animatables = getAnimatables(params.targets); + var animations = getAnimations(animatables, properties); + var timings = getInstanceTimings(animations, tweenSettings); + var id = instanceID; + instanceID++; + return mergeObjects(instanceSettings, { + id: id, + children: [], + animatables: animatables, + animations: animations, + duration: timings.duration, + delay: timings.delay, + endDelay: timings.endDelay, + }); + } + var activeInstances = []; + var pausedInstances = []; + var raf; + var engine = (function () { + function play() { + raf = requestAnimationFrame(step); + } + function step(t) { + var activeInstancesLength = activeInstances.length; + if (activeInstancesLength) { + var i = 0; + while (i < activeInstancesLength) { + var activeInstance = activeInstances[i]; + if (!activeInstance.paused) { + activeInstance.tick(t); + } else { + var instanceIndex = activeInstances.indexOf(activeInstance); + if (instanceIndex > -1) { + activeInstances.splice(instanceIndex, 1); + activeInstancesLength = activeInstances.length; + } + } + i++; + } + play(); + } else { + raf = cancelAnimationFrame(raf); + } + } + return play; + })(); + function anime(params) { + if (params === void 0) { + params = {}; + } + var startTime = 0, + lastTime = 0, + now = 0; + var children, + childrenLength = 0; + var resolve = null; + var speed = 1; + function makePromise(instance) { + var promise = + window.Promise && + new Promise(function (_resolve) { + return (resolve = _resolve); + }); + instance.finished = promise; + return promise; + } + var instance = createNewInstance(params); + var promise = makePromise(instance); + function toggleInstanceDirection() { + var direction = instance.direction; + if (direction !== "alternate") { + instance.direction = direction !== "normal" ? "normal" : "reverse"; + } + instance.reversed = !instance.reversed; + children.forEach(function (child) { + return (child.reversed = instance.reversed); + }); + } + function adjustTime(time) { + return instance.reversed ? instance.duration - time : time; + } + function resetTime() { + startTime = 0; + lastTime = adjustTime(instance.currentTime) * (1 / speed); + } + function seekChild(time, child) { + if (child) { + child.seek(time - child.timelineOffset); + } + } + function syncInstanceChildren(time) { + if (!instance.reversePlayback) { + for (var i = 0; i < childrenLength; i++) { + seekChild(time, children[i]); + } + } else { + for (var i$1 = childrenLength; i$1--; ) { + seekChild(time, children[i$1]); + } + } + } + function setAnimationsProgress(insTime) { + var i = 0; + var animations = instance.animations; + var animationsLength = animations.length; + while (i < animationsLength) { + var anim = animations[i]; + var animatable = anim.animatable; + var tweens = anim.tweens; + var tweenLength = tweens.length - 1; + var tween = tweens[tweenLength]; + if (tweenLength) { + tween = + filterArray(tweens, function (t) { + return insTime < t.end; + })[0] || tween; + } + var elapsed = + minMax(insTime - tween.start - tween.delay, 0, tween.duration) / + tween.duration; + var eased = isNaN(elapsed) ? 1 : tween.easing(elapsed); + var strings = tween.to.strings; + var round = tween.round; + var numbers = []; + var toNumbersLength = tween.to.numbers.length; + var progress = void 0; + for (var n = 0; n < toNumbersLength; n++) { + var value = void 0; + var toNumber = tween.to.numbers[n]; + var fromNumber = tween.from.numbers[n] || 0; + value = fromNumber + eased * (toNumber - fromNumber); + numbers.push(value); + } + var stringsLength = strings.length; + if (!stringsLength) { + progress = numbers[0]; + } else { + progress = strings[0]; + for (var s = 0; s < stringsLength; s++) { + var a = strings[s]; + var b = strings[s + 1]; + var n$1 = numbers[s]; + if (!isNaN(n$1)) { + if (!b) { + progress += n$1 + " "; + } else { + progress += n$1 + b; + } + } + } + } + setProgressValue[anim.type]( + animatable.target, + anim.property, + progress, + animatable.transforms, + ); + anim.currentValue = progress; + i++; + } + } + function setCallback(cb) { + if (instance[cb] && !instance.passThrough) { + instance[cb](instance); + } + if (cb == "update") { + self.refresh(); + } + } + function countIteration() { + if (instance.remaining && instance.remaining !== true) { + instance.remaining--; + } + } + function setInstanceProgress(engineTime) { + var insDuration = instance.duration; + var insDelay = instance.delay; + var insEndDelay = insDuration - instance.endDelay; + var insTime = adjustTime(engineTime); + instance.progress = minMax((insTime / insDuration) * 100, 0, 100); + instance.reversePlayback = insTime < instance.currentTime; + if (children) { + syncInstanceChildren(insTime); + } + if (!instance.began && instance.currentTime > 0) { + instance.began = true; + setCallback("begin"); + } + if (!instance.loopBegan && instance.currentTime > 0) { + instance.loopBegan = true; + setCallback("loopBegin"); + } + if (insTime <= insDelay && instance.currentTime !== 0) { + setAnimationsProgress(0); + } + if ( + (insTime >= insEndDelay && instance.currentTime !== insDuration) || + !insDuration + ) { + setAnimationsProgress(insDuration); + } + if (insTime > insDelay && insTime < insEndDelay) { + if (!instance.changeBegan) { + instance.changeBegan = true; + instance.changeCompleted = false; + setCallback("changeBegin"); + } + setCallback("change"); + setAnimationsProgress(insTime); + } else { + if (instance.changeBegan) { + instance.changeCompleted = true; + instance.changeBegan = false; + setCallback("changeComplete"); + } + } + instance.currentTime = minMax(insTime, 0, insDuration); + if (instance.began) { + setCallback("update"); + } + if (engineTime >= insDuration) { + lastTime = 0; + countIteration(); + if (!instance.remaining) { + instance.paused = true; + if (!instance.completed) { + instance.completed = true; + setCallback("loopComplete"); + setCallback("complete"); + if (!instance.passThrough && "Promise" in window) { + resolve(); + promise = makePromise(instance); + } + } + } else { + startTime = now; + setCallback("loopComplete"); + instance.loopBegan = false; + if (instance.direction === "alternate") { + toggleInstanceDirection(); + } + } + } + } + instance.reset = function () { + var direction = instance.direction; + instance.passThrough = false; + instance.currentTime = 0; + instance.progress = 0; + instance.paused = true; + instance.began = false; + instance.loopBegan = false; + instance.changeBegan = false; + instance.completed = false; + instance.changeCompleted = false; + instance.reversePlayback = false; + instance.reversed = direction === "reverse"; + instance.remaining = instance.loop; + children = instance.children; + childrenLength = children.length; + for (var i = childrenLength; i--; ) { + instance.children[i].reset(); + } + if ( + (instance.reversed && instance.loop !== true) || + (direction === "alternate" && instance.loop === 1) + ) { + instance.remaining++; + } + setAnimationsProgress(instance.reversed ? instance.duration : 0); + }; + instance.set = function (targets, properties) { + setTargetsValue(targets, properties); + return instance; + }; + instance.tick = function (t) { + now = t; + if (!startTime) { + startTime = now; + } + setInstanceProgress((now + (lastTime - startTime)) * speed); + }; + instance.seek = function (time) { + setInstanceProgress(adjustTime(time)); + }; + instance.pause = function () { + instance.paused = true; + resetTime(); + }; + instance.play = function () { + if (!instance.paused) { + return; + } + if (instance.completed) { + instance.reset(); + } + instance.paused = false; + activeInstances.push(instance); + resetTime(); + if (!raf) { + engine(); + } + }; + instance.reverse = function () { + toggleInstanceDirection(); + resetTime(); + }; + instance.restart = function () { + instance.reset(); + instance.play(); + }; + instance.reset(); + if (instance.autoplay) { + instance.play(); + } + instance.timeline = function (params) { + if (params === void 0) { + params = {}; + } + var tl = anime(params); + tl.duration = 0; + tl.add = function (instanceParams, timelineOffset) { + var tlIndex = activeInstances.indexOf(tl); + var children = tl.children; + if (tlIndex > -1) { + activeInstances.splice(tlIndex, 1); + } + function passThrough(ins) { + ins.passThrough = true; + } + for (var i = 0; i < children.length; i++) { + passThrough(children[i]); + } + var insParams = mergeObjects( + instanceParams, + replaceObjectProps(defaultTweenSettings, params), + ); + insParams.targets = insParams.targets || params.targets; + var tlDuration = tl.duration; + insParams.autoplay = false; + insParams.direction = tl.direction; + insParams.timelineOffset = is.und(timelineOffset) + ? tlDuration + : getRelativeValue(timelineOffset, tlDuration); + passThrough(tl); + tl.seek(insParams.timelineOffset); + var ins = anime(insParams); + passThrough(ins); + children.push(ins); + var timings = getInstanceTimings(children, params); + tl.delay = timings.delay; + tl.endDelay = timings.endDelay; + tl.duration = timings.duration; + tl.seek(0); + tl.reset(); + if (tl.autoplay) { + tl.play(); + } + return tl; + }; + return tl; + }; + return instance; + } + return anime(config); + }; + var VisGraph = VisualGraph; + if (typeof module !== "undefined" && typeof exports === "object") { + module.exports = VisGraph; + } else if (typeof define === "function" && (define.amd || define.cmd)) { + define(function () { + return VisGraph; + }); + } else { + this.VisGraph = VisGraph; + } +}).call(this || (typeof window !== "undefined" ? window : global)); diff --git a/kcui/src/assets/logo.png b/kcui/src/assets/logo.png new file mode 100644 index 0000000..f3d2503 Binary files /dev/null and b/kcui/src/assets/logo.png differ diff --git a/kcui/src/components/HelloWorld.vue b/kcui/src/components/HelloWorld.vue new file mode 100644 index 0000000..879051a --- /dev/null +++ b/kcui/src/components/HelloWorld.vue @@ -0,0 +1,58 @@ + + + + + + diff --git a/kcui/src/main.js b/kcui/src/main.js new file mode 100644 index 0000000..da99e79 --- /dev/null +++ b/kcui/src/main.js @@ -0,0 +1,16 @@ +import Vue from 'vue' +import App from './App.vue' +import VueRouter from 'vue-router'; + +import router from '../src/router/router.js' + + +Vue.config.productionTip = false + + +// 安装 Vue Router 插件 +Vue.use(VueRouter); +new Vue({ + router, // 将路由器传递给 Vue 实例 + render: h => h(App), +}).$mount('#app') diff --git a/kcui/src/router/router.js b/kcui/src/router/router.js new file mode 100644 index 0000000..b61e5e3 --- /dev/null +++ b/kcui/src/router/router.js @@ -0,0 +1,25 @@ +//1.导入vue 和 vuerouter 的包 +import Vue from 'vue' +import VueRouter from 'vue-router' +import Home from '@/view/Home.vue' +import Book from '@/view/Book.vue' + +//2.调用vue.use() 函数,把 VueRouter 安装为 Vue 的插件 +//vue.use()函数的作用,就是来安装插件的 +Vue.use(VueRouter) + + +export default new VueRouter({ + routes: [ //配置路由,这里是个数组 + { //每一个链接都是一个对象 + path: '/', //链接路径 + name: 'Home', //路由名称, + component: Home //对应的组件模板 + }, + { //每一个链接都是一个对象 + path: '/Book', //链接路径 + name: 'Book', //路由名称, + component: Book //对应的组件模板 + }, +] +}) diff --git a/kcui/src/utils/auth.js b/kcui/src/utils/auth.js new file mode 100644 index 0000000..aaa3b1c --- /dev/null +++ b/kcui/src/utils/auth.js @@ -0,0 +1,14 @@ +// utils/auth.js +import Cookies from 'js-cookie'; + +export const setToken = (token) => { + Cookies.set('satoken', token, { expires: 7, secure: true, sameSite: 'strict' }); // Token有效期为7天 +}; + +export const getToken = () => { + return Cookies.get('satoken'); +}; + +export const removeToken = () => { + Cookies.remove('satoken'); +}; diff --git a/kcui/src/utils/request.js b/kcui/src/utils/request.js new file mode 100644 index 0000000..b16a769 --- /dev/null +++ b/kcui/src/utils/request.js @@ -0,0 +1,137 @@ +import axios from 'axios'; + +// import { ElMessage } from "element-ui"; +// import router from '../router/router'; + +// create an axios instance +const service = axios.create({ + // baseURL: "http://127.0.0.1:10031/", // api 的 base_url + baseURL: "http://127.0.0.1:10031/", // api 的 base_urls + withCredentials: true, + timeout: 300000, // request timeout +}); + +// request interceptor +service.interceptors.request.use( + (config) => { + //config.headers['token'] = Auth.getToken() + return config; + }, + (error) => { + // Do something with request error + // console.log(error) // for debug + Promise.reject(error); + } +); + +// response interceptor +service.interceptors.response.use( + //response => response, + /** + * 下面的注释为通过在response里,自定义code来标示请求状态 + * 当code返回如下情况则说明权限有问题,登出并返回到登录页 + * 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中 + * 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除 + */ + // response => { + // const res = response.data + // if (res.code !== 20000) { + // Message({ + // message: res.message, + // type: 'error', + // duration: 5 * 1000 + // }) + // // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了; + // if (res.code === 50008 || res.code === 50012 || res.code === 50014) { + // // 请自行在引入 MessageBox + // // import { Message, MessageBox } from 'element-ui' + // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { + // confirmButtonText: '重新登录', + // cancelButtonText: '取消', + // type: 'warning' + // }).then(() => { + // store.dispatch('FedLogOut').then(() => { + // location.reload() // 为了重新实例化vue-router对象 避免bug + // }) + // }) + // } + // return Promise.reject('error') + // } else { + // return response.data + // } + // }, + (response) => { + let data; + // IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串) + if (response.data == undefined) { + data = JSON.parse(response.request.responseText); + } else { + data = response.data; + // const code = response.data.code + // if(code == '0') { + // data = response.data.data + // }else { + // Message({ + // message: response.data.msg, + // type: 'error', + // duration: 5 * 1000 + // }) + // return Promise.reject('error') + // } + } + return data; + }, + (error) => { + if (error && error.response) { + switch (error.response.status) { + case 400: + error.message = '数据请求错误'; + break; + case 401: + error.message = '未授权,请登录'; + break; + case 403: + error.message = '拒绝访问'; + break; + case 404: + error.message = '请求地址出错'; + break; + case 408: + error.message = '请求超时'; + break; + case 500: + error.message = '服务器内部错误'; + break; + case 501: + error.message = '服务未实现'; + break; + case 502: + error.message = '网关错误'; + break; + case 503: + error.message = '服务不可用'; + break; + case 504: + error.message = '网关超时'; + break; + case 505: + error.message = 'HTTP版本不受支持'; + break; + default: + } + } + // ElMessage({ + // message: error.message || '系统错误!', + // type: 'error', + // duration: 3 * 1000, + // }); + // if (error.response.status == 403) { + // // eslint-disable-next-line no-undef + // clearAllSession(); + // router.push('/'); + // } + // return Promise.reject(error); + } +); + +export default service; diff --git a/kcui/src/utils/util.js b/kcui/src/utils/util.js new file mode 100644 index 0000000..330ab23 --- /dev/null +++ b/kcui/src/utils/util.js @@ -0,0 +1,228 @@ +import Vue from "vue"; +let getevent = () => { + var Event = new Vue(); + return Event; +}; +const vueEvent = getevent(); +export { vueEvent }; +// 获取UUID +export function getUUID() { + var s = []; + var hexDigits = '0123456789abcdef'; + for (var i = 0; i < 36; i++) { + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); + } + s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010 + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 + s[8] = s[13] = s[18] = s[23] = '-'; + + var uuid = s.join(''); + return uuid; +} +//本地存储 +export function getlocal(name) { + let data = localStorage.getItem(name); + if (data != null && data != '') { + try { + let obj = eval('(' + data + ')'); + return obj; + } catch (e) { + return ''; + } + } else { + return ''; + } +} +export function getSession(name) { + let data = sessionStorage.getItem(name); + if (data != null && data != '') { + try { + let obj = eval('(' + data + ')'); + return obj; + } catch (e) { + return ''; + } + } else { + return ''; + } +} +export function setLocal(key, value) { + window.localStorage.setItem(key, JSON.stringify(value)); +} +export function setSession(key, value) { + window.sessionStorage.setItem(key, JSON.stringify(value)); +} +export function clearOneLocal(key) { + window.localStorage.removeItem(key); +} +export function clearOneSession(key) { + window.sessionStorage.removeItem(key); +} +export function clearAllLocal() { + window.localStorage.clear(); +} +export function clearAllSession() { + window.sessionStorage.clear(); +} +// 时间格式化 +export function parseTime(time, cFormat) { + if (arguments.length === 0) { + return null; + } + const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'; + let date; + if (typeof time === 'object') { + date = time; + } else { + if (typeof time === 'string' && /^[0-9]+$/.test(time)) { + time = parseInt(time); + } + if (typeof time === 'number' && time.toString().length === 10) { + time = time * 1000; + } + date = new Date(time); + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay(), + }; + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key]; + // Note: getDay() returns 0 on Sunday + if (key === 'a') { + return ['日', '一', '二', '三', '四', '五', '六'][value]; + } + if (result.length > 0 && value < 10) { + value = '0' + value; + } + return value || 0; + }); + return time_str; +} +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000; + } else { + time = +time; + } + const d = new Date(time); + const now = Date.now(); + + const diff = (now - d) / 1000; + + if (diff < 30) { + return '刚刚'; + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前'; + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前'; + } else if (diff < 3600 * 24 * 2) { + return '1天前'; + } + if (option) { + return parseTime(time, option); + } else { + return ( + d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分' + ); + } +} +//登录错误提示 +export function errortip(info) { + vueEvent.$message({ + message: info, + type: 'error', + duration: '2000', + }); +} +//成功提示 +export function successtip(info) { + vueEvent.$message({ + message: info, + type: 'success', + duration: '2000', + }); +} +//警告提示 +export function warningtip(info) { + vueEvent.$message({ + message: info, + type: 'warning', + duration: '2000', + }); +} +// 单位格式化 +export function numberFormat(value) { + var param = ''; + var k = 10000, + sizes = ['', '万', '亿', '万亿'], + i; + if (value < k) { + param = value; + } else { + i = Math.floor(Math.log(value) / Math.log(k)); + param = (value / Math.pow(k, i)).toFixed(1) + sizes[i]; + } + return param; +} +// 数据格式化 +export function converdata(limit) { + limit = limit * 1; + var size = ''; + if (limit < 1024) { + size = limit + 'KB'; + } else if (limit < 1024 * 1024) { + size = (limit / 1024).toFixed(1) + 'MB'; + } else if (limit < 1024 * 1024 * 1024) { + size = (limit / (1024 * 1024)).toFixed(1) + 'GB'; + } else { + size = (limit / (1024 * 1024 * 1024)).toFixed(1) + 'TB'; + } + return size; + // var sizestr = size + ""; + // var len = sizestr.indexOf("\."); + // var dec = sizestr.substr(len + 1, 2); + // if(dec == "0"){//当小数点后为00时 去掉小数部分 + // return sizestr.substring(0,len) + sizestr.substr(len + 3,2); + // } + // return sizestr; +} +// 单位格式化 +export function converunit(limit) { + limit = limit * 1; + var size = ''; + if (limit < 1024) { + size = 'KB'; + } else if (limit < 1024 * 1024) { + size = 'MB'; + } else if (limit < 1024 * 1024 * 1024) { + size = 'GB'; + } else { + size = 'TB'; + } + return size; +} +// 颜色转换 +// export function colorRgb(color) { +// var color = color.toLowerCase(); +// var pattern = /^#([0-9|a-f]{3}|[0-9|a-f]{6})$/; +// if (color && pattern.test(color)) { +// if (color.length == 4) { +// // 将三位转换为六位 +// color = '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3]; +// } +// //处理六位的颜色值 +// var colorNew = []; +// for (var i = 1; i < 7; i += 2) { +// colorNew.push(parseInt('0x' + color.slice(i, i + 2))); +// } +// return colorNew.join(','); +// } +// return color; +// } diff --git a/kcui/src/view/Book.vue b/kcui/src/view/Book.vue new file mode 100644 index 0000000..a7b68e9 --- /dev/null +++ b/kcui/src/view/Book.vue @@ -0,0 +1,274 @@ + + + diff --git a/kcui/src/view/Home.vue b/kcui/src/view/Home.vue new file mode 100644 index 0000000..bcbc787 --- /dev/null +++ b/kcui/src/view/Home.vue @@ -0,0 +1,276 @@ + + + diff --git a/kcui/vue.config.js b/kcui/vue.config.js new file mode 100644 index 0000000..910e297 --- /dev/null +++ b/kcui/vue.config.js @@ -0,0 +1,4 @@ +const { defineConfig } = require('@vue/cli-service') +module.exports = defineConfig({ + transpileDependencies: true +})