123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- /**
- * Graph data structure
- *
- * @module echarts/data/Graph
- * @author Yi Shen(https://www.github.com/pissang)
- */
- define(function(require) {
- var util = require('zrender/tool/util');
- 'use strict';
- /**
- * @alias module:echarts/data/Graph
- * @constructor
- * @param {boolean} directed
- */
- var Graph = function(directed) {
- /**
- * 是否是有向图
- * @type {boolean}
- * @private
- */
- this._directed = directed || false;
- /**
- * @type {Array}
- */
- this.nodes = [];
- this.edges = [];
- this._nodesMap = {};
- this._edgesMap = {};
- };
- /**
- * 是否是有向图
- * @return {boolean}
- */
- Graph.prototype.isDirected = function () {
- return this._directed;
- };
- /**
- * 添加一个新的节点
- * @param {string} id 节点名称
- * @param {*} [data] 存储的数据
- */
- Graph.prototype.addNode = function (id, data) {
- if (this._nodesMap[id]) {
- return this._nodesMap[id];
- }
- var node = new Graph.Node(id, data);
- this.nodes.push(node);
- this._nodesMap[id] = node;
- return node;
- };
-
- /**
- * 获取节点
- * @param {string} id
- * @return {module:echarts/data/Graph~Node}
- */
- Graph.prototype.getNodeById = function (id) {
- return this._nodesMap[id];
- };
- /**
- * 添加边
- * @param {string|module:echarts/data/Graph~Node} n1
- * @param {string|module:echarts/data/Graph~Node} n2
- * @param {*} data
- * @return {module:echarts/data/Graph~Edge}
- */
- Graph.prototype.addEdge = function (n1, n2, data) {
- if (typeof(n1) == 'string') {
- n1 = this._nodesMap[n1];
- }
- if (typeof(n2) == 'string') {
- n2 = this._nodesMap[n2];
- }
- if (!n1 || !n2) {
- return;
- }
- var key = n1.id + '-' + n2.id;
- if (this._edgesMap[key]) {
- return this._edgesMap[key];
- }
- var edge = new Graph.Edge(n1, n2, data);
- if (this._directed) {
- n1.outEdges.push(edge);
- n2.inEdges.push(edge);
- }
- n1.edges.push(edge);
- if (n1 !== n2) {
- n2.edges.push(edge);
- }
- this.edges.push(edge);
- this._edgesMap[key] = edge;
- return edge;
- };
- /**
- * 移除边
- * @param {module:echarts/data/Graph~Edge} edge
- */
- Graph.prototype.removeEdge = function (edge) {
- var n1 = edge.node1;
- var n2 = edge.node2;
- var key = n1.id + '-' + n2.id;
- if (this._directed) {
- n1.outEdges.splice(util.indexOf(n1.outEdges, edge), 1);
- n2.inEdges.splice(util.indexOf(n2.inEdges, edge), 1);
- }
- n1.edges.splice(util.indexOf(n1.edges, edge), 1);
- if (n1 !== n2) {
- n2.edges.splice(util.indexOf(n2.edges, edge), 1);
- }
- delete this._edgesMap[key];
- this.edges.splice(util.indexOf(this.edges, edge), 1);
- };
- /**
- * 获取边
- * @param {module:echarts/data/Graph~Node|string} n1
- * @param {module:echarts/data/Graph~Node|string} n2
- * @return {module:echarts/data/Graph~Edge}
- */
- Graph.prototype.getEdge = function (n1, n2) {
- if (typeof(n1) !== 'string') {
- n1 = n1.id;
- }
- if (typeof(n2) !== 'string') {
- n2 = n2.id;
- }
- if (this._directed) {
- return this._edgesMap[n1 + '-' + n2];
- } else {
- return this._edgesMap[n1 + '-' + n2]
- || this._edgesMap[n2 + '-' + n1];
- }
- };
- /**
- * 移除节点(及其邻接边)
- * @param {module:echarts/data/Graph~Node|string} node
- */
- Graph.prototype.removeNode = function (node) {
- if (typeof(node) === 'string') {
- node = this._nodesMap[node];
- if (!node) {
- return;
- }
- }
- delete this._nodesMap[node.id];
- this.nodes.splice(util.indexOf(this.nodes, node), 1);
- for (var i = 0; i < this.edges.length;) {
- var edge = this.edges[i];
- if (edge.node1 === node || edge.node2 === node) {
- this.removeEdge(edge);
- } else {
- i++;
- }
- }
- };
- /**
- * 遍历并且过滤指定的节点
- * @param {Function} cb
- * @param {*} [context]
- */
- Graph.prototype.filterNode = function (cb, context) {
- var len = this.nodes.length;
- for (var i = 0; i < len;) {
- if (cb.call(context, this.nodes[i], i)) {
- i++;
- } else {
- this.removeNode(this.nodes[i]);
- len--;
- }
- }
- };
- /**
- * 遍历并且过滤指定的边
- * @param {Function} cb
- * @param {*} [context]
- */
- Graph.prototype.filterEdge = function (cb, context) {
- var len = this.edges.length;
- for (var i = 0; i < len;) {
- if (cb.call(context, this.edges[i], i)) {
- i++;
- } else {
- this.removeEdge(this.edges[i]);
- len--;
- }
- }
- };
- /**
- * 线性遍历所有节点
- * @param {Function} cb
- * @param {*} [context]
- */
- Graph.prototype.eachNode = function (cb, context) {
- var len = this.nodes.length;
- for (var i = 0; i < len; i++) {
- if (this.nodes[i]) { // 可能在遍历过程中存在节点删除
- cb.call(context, this.nodes[i], i);
- }
- }
- };
-
- /**
- * 线性遍历所有边
- * @param {Function} cb
- * @param {*} [context]
- */
- Graph.prototype.eachEdge = function (cb, context) {
- var len = this.edges.length;
- for (var i = 0; i < len; i++) {
- if (this.edges[i]) { // 可能在遍历过程中存在边删除
- cb.call(context, this.edges[i], i);
- }
- }
- };
-
- /**
- * 清空图
- */
- Graph.prototype.clear = function() {
- this.nodes.length = 0;
- this.edges.length = 0;
- this._nodesMap = {};
- this._edgesMap = {};
- };
-
- /**
- * 广度优先遍历
- * @param {Function} cb
- * @param {module:echarts/data/Graph~Node} startNode 遍历起始节点
- * @param {string} [direction=none] none, in, out 指定遍历边
- * @param {*} [context] 回调函数调用context
- */
- Graph.prototype.breadthFirstTraverse = function (
- cb, startNode, direction, context
- ) {
- if (typeof(startNode) === 'string') {
- startNode = this._nodesMap[startNode];
- }
- if (!startNode) {
- return;
- }
- var edgeType = 'edges';
- if (direction === 'out') {
- edgeType = 'outEdges';
- } else if (direction === 'in') {
- edgeType = 'inEdges';
- }
-
- for (var i = 0; i < this.nodes.length; i++) {
- this.nodes[i].__visited = false;
- }
- if (cb.call(context, startNode, null)) {
- return;
- }
- var queue = [startNode];
- while (queue.length) {
- var currentNode = queue.shift();
- var edges = currentNode[edgeType];
- for (var i = 0; i < edges.length; i++) {
- var e = edges[i];
- var otherNode = e.node1 === currentNode
- ? e.node2 : e.node1;
- if (!otherNode.__visited) {
- if (cb.call(otherNode, otherNode, currentNode)) {
- // Stop traversing
- return;
- }
- queue.push(otherNode);
- otherNode.__visited = true;
- }
- }
- }
- };
- /**
- * 复制图
- */
- Graph.prototype.clone = function () {
- var graph = new Graph(this._directed);
- for (var i = 0; i < this.nodes.length; i++) {
- graph.addNode(this.nodes[i].id, this.nodes[i].data);
- }
- for (var i = 0; i < this.edges.length; i++) {
- var e = this.edges[i];
- graph.addEdge(e.node1.id, e.node2.id, e.data);
- }
- return graph;
- };
- /**
- * 图节点
- * @alias module:echarts/data/Graph~Node
- * @param {string} id
- * @param {*} [data]
- */
- var Node = function(id, data) {
- /**
- * 节点名称
- * @type {string}
- */
- this.id = id;
- /**
- * 节点存储的数据
- * @type {*}
- */
- this.data = data || null;
- /**
- * 入边,只在有向图上有效
- * @type {Array.<module:echarts/data/Graph~Edge>}
- */
- this.inEdges = [];
- /**
- * 出边,只在有向图上有效
- * @type {Array.<module:echarts/data/Graph~Edge>}
- */
- this.outEdges = [];
- /**
- * 邻接边
- * @type {Array.<module:echarts/data/Graph~Edge>}
- */
- this.edges = [];
- };
-
- /**
- * 度
- * @return {number}
- */
- Node.prototype.degree = function() {
- return this.edges.length;
- };
-
- /**
- * 入度,只在有向图上有效
- * @return {number}
- */
- Node.prototype.inDegree = function() {
- return this.inEdges.length;
- };
-
- /**
- * 出度,只在有向图上有效
- * @return {number}
- */
- Node.prototype.outDegree = function() {
- return this.outEdges.length;
- };
- /**
- * 图边
- * @alias module:echarts/data/Graph~Edge
- * @param {module:echarts/data/Graph~Node} node1
- * @param {module:echarts/data/Graph~Node} node2
- * @param {extra} data
- */
- var Edge = function(node1, node2, data) {
- /**
- * 节点1,如果是有向图则为源节点
- * @type {module:echarts/data/Graph~Node}
- */
- this.node1 = node1;
- /**
- * 节点2,如果是有向图则为目标节点
- * @type {module:echarts/data/Graph~Node}
- */
- this.node2 = node2;
- /**
- * 边存储的数据
- * @type {*}
- */
- this.data = data || null;
- };
- Graph.Node = Node;
- Graph.Edge = Edge;
- /**
- * 从邻接矩阵生成
- * ```
- * TARGET
- * -1--2--3--4--5-
- * 1| x x x x x
- * 2| x x x x x
- * 3| x x x x x SOURCE
- * 4| x x x x x
- * 5| x x x x x
- * ```
- * 节点的行列总和会被写到`node.data.value`
- * 对于有向图会计算每一行的和写到`node.data.outValue`,
- * 计算每一列的和写到`node.data.inValue`。
- * 边的权重会被然后写到`edge.data.weight`。
- *
- * @method module:echarts/data/Graph.fromMatrix
- * @param {Array.<Object>} nodesData 节点信息,必须有`id`属性, 会保存到`node.data`中
- * @param {Array} matrix 邻接矩阵
- * @param {boolean} directed 是否是有向图
- * @return {module:echarts/data/Graph}
- */
- Graph.fromMatrix = function(nodesData, matrix, directed) {
- if (
- !matrix || !matrix.length
- || (matrix[0].length !== matrix.length)
- || (nodesData.length !== matrix.length)
- ) {
- // Not a valid data
- return;
- }
- var size = matrix.length;
- var graph = new Graph(directed);
- for (var i = 0; i < size; i++) {
- var node = graph.addNode(nodesData[i].id, nodesData[i]);
- // TODO
- // node.data已经有value的情况
- node.data.value = 0;
- if (directed) {
- node.data.outValue = node.data.inValue = 0;
- }
- }
- for (var i = 0; i < size; i++) {
- for (var j = 0; j < size; j++) {
- var item = matrix[i][j];
- if (directed) {
- graph.nodes[i].data.outValue += item;
- graph.nodes[j].data.inValue += item;
- }
- graph.nodes[i].data.value += item;
- graph.nodes[j].data.value += item;
- }
- }
- for (var i = 0; i < size; i++) {
- for (var j = i; j < size; j++) {
- var item = matrix[i][j];
- if (item === 0) {
- continue;
- }
- var n1 = graph.nodes[i];
- var n2 = graph.nodes[j];
- var edge = graph.addEdge(n1, n2, {});
- edge.data.weight = item;
- if (i !== j) {
- if (directed && matrix[j][i]) {
- var inEdge = graph.addEdge(n2, n1, {});
- inEdge.data.weight = matrix[j][i];
- }
- }
- }
- }
- // console.log(graph.edges.map(function (e) {return e.node1.id + '-' + e.node2.id;}))
- return graph;
- };
- return Graph;
- });
|