/** * Tree data structure * * @module echarts/data/Tree * @author Yi Shen(https://www.github.com/pissang) */ define(function(require) { var zrUtil = require('zrender/tool/util'); /** * @constructor module:echarts/data/Tree~TreeNode * @param {string} id Node ID * @param {Object} [data] */ function TreeNode(id, data) { /** * @type {string} */ this.id = id; /** * 节点的深度 * @type {number} */ this.depth = 0; /** * 以当前节点为根节点的子树的高度 * @type {number} */ this.height = 0; /** * 子节点列表 * @type {Array.} */ this.children = []; /** * @type {module:echarts/data/Tree~TreeNode} */ this.parent = null; /** * 存储的用户数据 * @type {Object} */ this.data = data || null; } /** * 添加子节点 * @param {module:echarts/data/Tree~TreeNode} child */ TreeNode.prototype.add = function (child) { var children = this.children; if (child.parent === this) { return; } children.push(child); child.parent = this; }; /** * 移除子节点 * @param {module:echarts/data/Tree~TreeNode} child */ TreeNode.prototype.remove = function (child) { var children = this.children; var idx = zrUtil.indexOf(children, child); if (idx >= 0) { children.splice(idx, 1); child.parent = null; } }; /** * 遍历当前节点及其所有子节点 * @param {Function} cb * @param {Object} [context] */ TreeNode.prototype.traverse = function (cb, context) { cb.call(context, this); for (var i = 0; i < this.children.length; i++) { this.children[i].traverse(cb, context); } }; /** * 更新当前树及所有子树的高度和深度 * @param {number} depth */ TreeNode.prototype.updateDepthAndHeight = function (depth) { var height = 0; this.depth = depth; for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; child.updateDepthAndHeight(depth + 1); if (child.height > height) { height = child.height; } } this.height = height + 1; }; /** * @param {string} id * @return module:echarts/data/Tree~TreeNode */ TreeNode.prototype.getNodeById = function (id) { if (this.id === id) { return this; } for (var i = 0; i < this.children.length; i++) { var res = this.children[i].getNodeById(id); if (res) { return res; } } }; /** * @constructor * @alias module:echarts/data/Tree * @param {string} id */ function Tree(id) { /** * @type {module:echarts/data/Tree~TreeNode} */ this.root = new TreeNode(id); } /** * 遍历树的所有子节点 * @param {Function} cb * @param {Object} [context] */ Tree.prototype.traverse = function(cb, context) { this.root.traverse(cb, context); }; /** * 生成子树 * @param {string} id 子树根节点 id * @return {module:echarts/data/Tree} */ Tree.prototype.getSubTree = function(id) { var root = this.getNodeById(id); if (root) { var tree = new Tree(root.id); tree.root = root; return tree; } }; /** * @param {string} id * @return module:echarts/data/Tree~TreeNode */ Tree.prototype.getNodeById = function (id) { return this.root.getNodeById(id); }; /** * 从 option 里的 data 数据构建树 * @param {string} id * @param {Array.} data * @return module:echarts/data/Tree */ Tree.fromOptionData = function (id, data) { var tree = new Tree(id); var rootNode = tree.root; // Root node rootNode.data = { name: id, children: data }; function buildHierarchy(dataNode, parentNode) { var node = new TreeNode(dataNode.name, dataNode); parentNode.add(node); // 遍历添加子节点 var children = dataNode.children; if (children) { for (var i = 0; i < children.length; i++) { buildHierarchy(children[i], node); } } } for (var i = 0; i < data.length; i++) { buildHierarchy(data[i], rootNode); } tree.root.updateDepthAndHeight(0); return tree; }; // TODO Tree.fromGraph = function (graph) { function buildHierarchy(root) { var graphNode = graph.getNodeById(root.id); for (var i = 0; i < graphNode.outEdges.length; i++) { var edge = graphNode.outEdges[i]; var childTreeNode = treeNodesMap[edge.node2.id]; root.children.push(childTreeNode); buildHierarchy(childTreeNode); } } var treeMap = {}; var treeNodesMap = {}; for (var i = 0; i < graph.nodes.length; i++) { var node = graph.nodes[i]; var treeNode; if (node.inDegree() === 0) { treeMap[node.id] = new Tree(node.id); treeNode = treeMap[node.id].root; } else { treeNode = new TreeNode(node.id); } treeNode.data = node.data; treeNodesMap[node.id] = treeNode; } var treeList = []; for (var id in treeMap) { buildHierarchy(treeMap[id].root); treeMap[id].root.updateDepthAndHeight(0); treeList.push(treeMap[id]); } return treeList; }; return Tree; });