当前位置: 代码迷 >> 图形/图像 >> 图形格局-Layout 之js设计实现
  详细解决方案

图形格局-Layout 之js设计实现

热度:6670   发布时间:2013-02-26 00:00:00.0
图形布局-Layout 之js设计实现

前言

定位browser 的 chart,   VML,SVG, HTML5 Canvas使用的方式各不一样。

如果使用现有的js  library (各种实现js 图表的library汇总与比较) , 调用的API方式也肯定不同。

举个例子: draw2d 使用addFigure 和 setPosition 都可以设置图的位置。

混在特定技术或是特定library 里去layout , 很明显不是一个明智之举。

切分开来, layout 的功能对于任何的图形绘制都适用。就是本章所讨论的了。

实现思想


其实实现思想很简单,维护一个JS 的object(Graph)。 在这个Object 里记录节点,边的信息; 
节点包含有如下信息:
      id  -- 表示符
      x  -- 横坐标
      y  -- 纵坐标
     shape -- 绘制的图
这样的话, 在绘制一个图节点之前, 先要在这个Graph 维护这个图节点的一些信息。

Graph 示例


var Graph = function() {	this.nodeSet = {};	this.nodes = [];	this.edges = [];	this.adjacency = {};	this.nextNodeId = 0;	this.nextEdgeId = 0;	this.eventListeners = [];};var Node = function(id, data) {	this.id = id;	this.data = typeof(data) !== 'undefined' ? data : {};};var Edge = function(id, source, target, data) {	this.id = id;	this.source = source;	this.target = target;	this.data = typeof(data) !== 'undefined' ? data : {};};Graph.prototype.addNode = function(node) {	if (typeof(this.nodeSet[node.id]) === 'undefined') {		this.nodes.push(node);	}	this.nodeSet[node.id] = node;	this.notify();	return node;};Graph.prototype.addEdge = function(edge) {	var exists = false;	this.edges.forEach(function(e) {		if (edge.id === e.id) { exists = true; }	});	if (!exists) {		this.edges.push(edge);	}	if (typeof(this.adjacency[edge.source.id]) === 'undefined') {		this.adjacency[edge.source.id] = {};	}	if (typeof(this.adjacency[edge.source.id][edge.target.id]) === 'undefined') {		this.adjacency[edge.source.id][edge.target.id] = [];	}	exists = false;	this.adjacency[edge.source.id][edge.target.id].forEach(function(e) {			if (edge.id === e.id) { exists = true; }	});	if (!exists) {		this.adjacency[edge.source.id][edge.target.id].push(edge);	}	this.notify();	return edge;};Graph.prototype.newNode = function(data) {	var node = new Node(this.nextNodeId++, data);	this.addNode(node);	return node;};Graph.prototype.newEdge = function(source, target, data) {	var edge = new Edge(this.nextEdgeId++, source, target, data);	this.addEdge(edge);	return edge;};// find the edges from node1 to node2Graph.prototype.getEdges = function(node1, node2) {	if (typeof(this.adjacency[node1.id]) !== 'undefined'		&& typeof(this.adjacency[node1.id][node2.id]) !== 'undefined') {		return this.adjacency[node1.id][node2.id];	}	return [];};// remove a node and it's associated edges from the graphGraph.prototype.removeNode = function(node) {	if (typeof(this.nodeSet[node.id]) !== 'undefined') {		delete this.nodeSet[node.id];	}	for (var i = this.nodes.length - 1; i >= 0; i--) {		if (this.nodes[i].id === node.id) {			this.nodes.splice(i, 1);		}	}	this.detachNode(node);};// removes edges associated with a given nodeGraph.prototype.detachNode = function(node) {	var tmpEdges = this.edges.slice();	tmpEdges.forEach(function(e) {		if (e.source.id === node.id || e.target.id === node.id) {			this.removeEdge(e);		}	}, this);	this.notify();};// remove a node and it's associated edges from the graphGraph.prototype.removeEdge = function(edge) {	for (var i = this.edges.length - 1; i >= 0; i--) {		if (this.edges[i].id === edge.id) {			this.edges.splice(i, 1);		}	}	for (var x in this.adjacency) {		for (var y in this.adjacency[x]) {			var edges = this.adjacency[x][y];			for (var j=edges.length - 1; j>=0; j--) {				if (this.adjacency[x][y][j].id === edge.id) {					this.adjacency[x][y].splice(j, 1);				}			}		}	}	this.notify();};/* Merge a list of nodes and edges into the current graph. eg.var o = {	nodes: [		{id: 123, data: {type: 'user', userid: 123, displayname: 'aaa'}},		{id: 234, data: {type: 'user', userid: 234, displayname: 'bbb'}}	],	edges: [		{from: 0, to: 1, type: 'submitted_design', directed: true, data: {weight: }}	]}*/Graph.prototype.merge = function(data) {	var nodes = [];	data.nodes.forEach(function(n) {		nodes.push(this.addNode(new Node(n.id, n.data)));	}, this);	data.edges.forEach(function(e) {		var from = nodes[e.from];		var to = nodes[e.to];		var id = (e.directed)			? (id = e.type + "-" + from.id + "-" + to.id)			: (from.id < to.id) // normalise id for non-directed edges				? e.type + "-" + from.id + "-" + to.id				: e.type + "-" + to.id + "-" + from.id;		var edge = this.addEdge(new Edge(id, from, to, e.data));		edge.data.type = e.type;	}, this);};Graph.prototype.filterNodes = function(fn) {	var tmpNodes = this.nodes.slice();	tmpNodes.forEach(function(n) {		if (!fn(n)) {			this.removeNode(n);		}	}, this);};Graph.prototype.filterEdges = function(fn) {	var tmpEdges = this.edges.slice();	tmpEdges.forEach(function(e) {		if (!fn(e)) {			this.removeEdge(e);		}	}, this);};Graph.prototype.addGraphListener = function(obj) {	this.eventListeners.push(obj);};Graph.prototype.notify = function() {	this.eventListeners.forEach(function(obj){		obj.graphChanged();	});};