提交 56a6258d 编写于 作者: L lang

Rewrite Graph

上级 64917603
......@@ -8,7 +8,7 @@ define(function(require) {
'use strict';
var util = require('zrender/core/util');
var zrUtil = require('zrender/core/util');
var Model = require('../model/Model');
/**
......@@ -25,94 +25,100 @@ define(function(require) {
this._directed = directed || false;
/**
* @type {Array.<module:echarts/data/Graph~Node>}
* @type {Array.<module:echarts/data/Graph.Node>}
* @readOnly
*/
this.nodes = [];
/**
* @type {Array.<module:echarts/data/Graph~Edge>}
* @type {Array.<module:echarts/data/Graph.Edge>}
* @readOnly
*/
this.edges = [];
/**
* @type {Object.<string, module:echarts/data/Graph~Node>}
* @type {Object.<string, module:echarts/data/Graph.Node>}
* @private
*/
this._nodesMap = {};
/**
* @type {Object.<string, module:echarts/data/Graph~Edge>}
* @type {Object.<string, module:echarts/data/Graph.Edge>}
* @private
*/
this._edgesMap = {};
};
var graphProto = Graph.prototype;
/**
* @type {string}
*/
Graph.prototype.type = 'graph';
graphProto.type = 'graph';
/**
* 是否是有向图
* If is directed graph
* @return {boolean}
*/
Graph.prototype.isDirected = function () {
graphProto.isDirected = function () {
return this._directed;
};
/**
* 添加一个新的节点
* @param {*} [option] 存储的数据
* Add a new node
* @param {string} name
* @param {number} [dataIndex]
*/
Graph.prototype.addNode = function (name, option) {
graphProto.addNode = function (name, dataIndex) {
var nodesMap = this._nodesMap;
if (this._nodesMap[name]) {
return this._nodesMap[name];
if (nodesMap[name]) {
return nodesMap[name];
}
var node = new Graph.Node(option);
node.name = name;
var node = new Node(name, dataIndex);
node.hostGraph = this;
this.nodes.push(node);
this._nodesMap[name] = node;
nodesMap[name] = node;
return node;
};
/**
* 获取节点
* Get node by name
* @param {string} name
* @return {module:echarts/data/Graph~Node}
* @return {module:echarts/data/Graph.Node}
*/
Graph.prototype.getNodeByName = function (name) {
graphProto.getNodeByName = function (name) {
return this._nodesMap[name];
};
/**
* 添加边
* @param {string|module:echarts/data/Graph~Node} n1
* @param {string|module:echarts/data/Graph~Node} n2
* @param {*} option
* @return {module:echarts/data/Graph~Edge}
* Add a new edge
* @param {string|module:echarts/data/Graph.Node} n1
* @param {string|module:echarts/data/Graph.Node} n2
* @param {number} [dataIndex=-1]
* @return {module:echarts/data/Graph.Edge}
*/
Graph.prototype.addEdge = function (n1, n2, option) {
if (typeof(n1) == 'string') {
n1 = this._nodesMap[n1];
graphProto.addEdge = function (n1, n2, dataIndex) {
var nodesMap = this._nodesMap;
var edgesMap = this._edgesMap;
if (typeof n1 == 'string') {
n1 = nodesMap[n1];
}
if (typeof(n2) == 'string') {
n2 = this._nodesMap[n2];
if (typeof n2 == 'string') {
n2 = nodesMap[n2];
}
if (!n1 || !n2) {
return;
}
var key = n1.name + '-' + n2.name;
if (this._edgesMap[key]) {
return this._edgesMap[key];
if (edgesMap[key]) {
return edgesMap[key];
}
var edge = new Graph.Edge(option);
edge.node1 = n1;
edge.node2 = n2;
var edge = new Edge(n1, n2, dataIndex);
if (this._directed) {
n1.outEdges.push(edge);
......@@ -124,39 +130,18 @@ define(function(require) {
}
this.edges.push(edge);
this._edgesMap[key] = edge;
edgesMap[key] = edge;
return edge;
};
/**
* 移除边
* @param {module:echarts/data/Graph~Edge} edge
* Get edge by two nodes
* @param {module:echarts/data/Graph.Node|string} n1
* @param {module:echarts/data/Graph.Node|string} n2
* @return {module:echarts/data/Graph.Edge}
*/
Graph.prototype.removeEdge = function (edge) {
var n1 = edge.node1;
var n2 = edge.node2;
var key = n1.name + '-' + n2.name;
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) {
graphProto.getEdge = function (n1, n2) {
if (typeof(n1) !== 'string') {
n1 = n1.name;
}
......@@ -164,120 +149,57 @@ define(function(require) {
n2 = n2.name;
}
var edgesMap = this._edgesMap;
if (this._directed) {
return this._edgesMap[n1 + '-' + n2];
return 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.name];
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++;
}
return edgesMap[n1 + '-' + n2]
|| edgesMap[n2 + '-' + n1];
}
};
/**
* 遍历并且过滤指定的节点
* @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--;
}
}
};
/**
* 线性遍历所有节点
* Iterate all nodes
* @param {Function} cb
* @param {*} [context]
* @param {*} [context]
*/
Graph.prototype.eachNode = function (cb, context) {
var len = this.nodes.length;
graphProto.eachNode = function (cb, context) {
var nodes = this.nodes;
var len = nodes.length;
for (var i = 0; i < len; i++) {
if (this.nodes[i]) { // 可能在遍历过程中存在节点删除
cb.call(context, this.nodes[i], i);
if (nodes[i].dataIndex >= 0) {
cb.call(context, nodes[i], i);
}
}
};
/**
* 线性遍历所有边
* Iterate all edges
* @param {Function} cb
* @param {*} [context]
* @param {*} [context]
*/
Graph.prototype.eachEdge = function (cb, context) {
var len = this.edges.length;
graphProto.eachEdge = function (cb, context) {
var edges = this.edges;
var len = edges.length;
for (var i = 0; i < len; i++) {
if (this.edges[i]) { // 可能在遍历过程中存在边删除
cb.call(context, this.edges[i], i);
if (edges[i].dataIndex >= 0
&& edges[i].node1.dataIndex >= 0
&& edges[i].node2.dataIndex >= 0
) {
cb.call(context, edges[i], i);
}
}
};
/**
* 清空图
*/
Graph.prototype.clear = function () {
this.nodes.length = 0;
this.edges.length = 0;
this._nodesMap = {};
this._edgesMap = {};
};
/**
* 广度优先遍历
* Breadth first traverse
* @param {Function} cb
* @param {module:echarts/data/Graph~Node} startNode 遍历起始节点
* @param {string} [direction=none] none, in, out 指定遍历边
* @param {*} [context] 回调函数调用context
* @param {module:echarts/data/Graph.Node} startNode
* @param {string} [direction='none'] 'none'|'in'|'out'
* @param {*} [context]
*/
Graph.prototype.breadthFirstTraverse = function (
graphProto.breadthFirstTraverse = function (
cb, startNode, direction, context
) {
if (typeof(startNode) === 'string') {
......@@ -287,12 +209,8 @@ define(function(require) {
return;
}
var edgeType = 'edges';
if (direction === 'out') {
edgeType = 'outEdges';
} else if (direction === 'in') {
edgeType = 'inEdges';
}
var edgeType = direction === 'out'
? 'outEdges' : (direction === 'in' ? 'inEdges' : 'edges');
for (var i = 0; i < this.nodes.length; i++) {
this.nodes[i].__visited = false;
......@@ -323,19 +241,39 @@ define(function(require) {
}
};
// TODO
graphProto.depthFirstTraverse = function (
cb, startNode, direction, context
) {
};
graphProto.update = function () {
var data = this.data;
var nodes = this.nodes;
for (var i = 0, len = nodes.length; i < len; i++) {
nodes[i].dataIndex = -1;
}
for (var i = 0, len = data.count(); i < len; i++) {
nodes[data.getRawIndex(i)].dataIndex = i;
}
};
/**
* 复制图
* @return {module:echarts/data/Graph}
*/
Graph.prototype.clone = function () {
graphProto.clone = function () {
var graph = new Graph(this._directed);
var nodes = this.nodes;
var edges = this.edges;
for (var i = 0; i < nodes.length; i++) {
graph.addNode(nodes[i].name, nodes[i].data);
graph.addNode(nodes[i].name, nodes[i].dataIndex);
}
for (var i = 0; i < edges.length; i++) {
var e = edges[i];
graph.addEdge(e.node1.name, e.node2.name, e.data);
graph.addEdge(e.node1.name, e.node2.name, e.dataIndex);
}
return graph;
};
......@@ -343,169 +281,149 @@ define(function(require) {
/**
* 图节点
* @alias module:echarts/data/Graph~Node
* @alias module:echarts/data/Graph.Node
*/
var Node = Model.extend({
init: function (option) {
/**
* 节点名称
* @type {string}
*/
this.name = option.name || '';
/**
* 节点存储的数据
* @type {*}
*/
this.option = option || 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 = [];
},
function Node(name, dataIndex) {
/**
* @type {string}
*/
this.name = name || '';
/**
* 度
* @return {number}
* @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 = [];
/**
* @type {module:echarts/data/Graph}
*/
this.hostGraph;
};
Node.prototype = {
constructor: Node,
/**
* @return {number}
*/
degree: function () {
return this.edges.length;
},
/**
* 入度,只在有向图上有效
* @return {number}
*/
* @return {number}
*/
inDegree: function () {
return this.inEdges.length;
},
/**
* 出度,只在有向图上有效
* @return {number}
*/
outDegree: function () {
return this.outEdges.length;
},
/**
* @param {string} [path]
* @return {module:echarts/model/Model}
*/
getModel: function (path) {
if (this.dataIndex < 0) {
return;
}
var graph = this.hostGraph;
var itemModel = graph.data.getItemModel(this.dataIndex);
return itemModel.getModel(path);
},
// Proxy methods
/**
* @param {string=} [dimension='value'] Default 'value'. can be 'a', 'b', 'c', 'd', 'e'.
* @return {number}
*/
getValue: function (dimension) {
return this.hostTree.data.get(dimension || 'value', this.dataIndex);
},
/**
* @param {Object|string} key
* @param {*} [value]
*/
setVisual: function (key, value) {
this.dataIndex >= 0
&& this.hostGraph.data.setItemVisual(this.dataIndex, key, value);
},
/**
* @param {string} key
* @return {boolean}
*/
getVisual: function (key, ignoreParent) {
return this.hostGraph.data.getItemVisual(this.dataIndex, key, ignoreParent)
},
/**
* @param {Object} layout
* @return {boolean} [merge=false]
*/
setLayout: function (layout, merge) {
this.dataIndex >= 0
&& this.hostGraph.data.setItemLayout(this.dataIndex, layout, merge);
},
/**
* @return {Object}
*/
getLayout: function () {
return this.hostGraph.data.getItemLayout(this.dataIndex);
},
/**
* @return {number}
*/
getRawIndex: function () {
return this.hostGraph.data.getRawIndex(this.dataIndex);
}
});
};
/**
* 图边
* @alias module:echarts/data/Graph~Edge
* @param {module:echarts/data/Graph~Node} node1
* @param {module:echarts/data/Graph~Node} node2
* @param {extra} data
* @alias module:echarts/data/Graph.Edge
* @param {module:echarts/data/Graph.Node} n1
* @param {module:echarts/data/Graph.Node} n2
* @param {number} [dataIndex=-1]
*/
var Edge = Model.extend({
function Edge(n1, n2, dataIndex) {
/**
* 节点1,如果是有向图则为源节点
* @type {module:echarts/data/Graph~Node}
* @type {module:echarts/data/Graph.Node}
*/
node1: null,
this.node1 = n1;
/**
* 节点2,如果是有向图则为目标节点
* @type {module:echarts/data/Graph~Node}
* @type {module:echarts/data/Graph.Node}
*/
node2: null,
this.node2 = n2;
init: function (option) {
this.option = option;
}
});
this.dataIndex = dataIndex == null ? -1 : dataIndex;
};
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.option.value`
* 对于有向图会计算每一行的和写到`node.option.outValue`,
* 计算每一列的和写到`node.option.inValue`。
* 边的权重会被然后写到`edge.option.weight`。
*
* @method module:echarts/data/Graph.fromMatrix
* @param {Array.<Object>} nodesData 节点信息,必须有`name`属性, 会保存到`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 valname data
return;
}
var size = matrix.length;
var graph = new Graph(directed);
for (var i = 0; i < size; i++) {
var node = graph.addNode(nodesData[i].name, nodesData[i]);
// TODO
// node.data已经有value的情况
node.option.value = 0;
if (directed) {
node.option.outValue = node.option.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].option.outValue += item;
graph.nodes[j].option.inValue += item;
}
graph.nodes[i].option.value += item;
graph.nodes[j].option.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.option.weight = item;
if (i !== j) {
if (directed && matrix[j][i]) {
var inEdge = graph.addEdge(n2, n1, {});
inEdge.option.weight = matrix[j][i];
}
}
}
}
return graph;
};
return Graph;
});
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册