var tree, width, height, clock, lastCalledTime, root, node,
\t repulsionFactor = 10000,
\t nodes = new Array();
function setup(){
\t tree = new Tree(200,200);
\t tree.repulsionFactor = 10000;
\t for(var i=0;i<5;i++){
\t \t tree.addRandomChild();
\t }
\t for(var i=0;i<10;i++){
\t \t tree.addRandomChild(1);
\t }
\t for(var i=0;i<15;i++){
\t \t tree.addRandomChild(2);
\t }
\t root = tree.root;
\t node = root.children[0];
\t var canvas = document.getElementById('canvas');
\t width = 400;
\t height = 400;
\t canvas.width = width;
\t canvas.height = height;
\t canvasSize = {x:window.innerWidth-5,y:window.innerHeight-5};
\t ctx = canvas.getContext('2d');
\t clock = requestAnimationFrame(main);
\t //clock = setInterval(main,200);
}
function main(){
\t if(!lastCalledTime) {
\t \t lastCalledTime = Date.now();
\t }
\t ctx.clearRect(0,0,width,height);
\t tree.repulsion();
\t tree.draw();
\t var delta = (Date.now() - lastCalledTime)/1000;
\t lastCalledTime = Date.now();
\t var fps = 1/delta;
tree.repulsionFactor*=0.99;
\t clock = requestAnimationFrame(main);
}
function Tree(x,y){
\t this.x = x;
\t this.y = y;
\t this.nodeColor = "gray";
\t this.lineColor = "black";
\t this.size = 20;
\t this.lineWidth = 3;
\t this.linkLength = 100;
\t this.nodes = new Array();
\t this.repulsionFactor = 10000;
\t this.root = new Node(this,this,"root");
}
Tree.prototype.addRandomChild = function(level=0){
\t var node = this.root;
\t for(var i=0;i<level;i++){
\t \t if(node.children.length>0){
\t \t \t var randIndex = Math.floor(node.children.length*Math.random());
\t \t \t node = node.children[randIndex];
\t \t }else{
\t \t \t return false;
\t \t }
\t }
\t node.addChild();
};
Tree.prototype.draw = function(){
\t this.root.draw();
}
Tree.prototype.repulsion = function(){
\t this.root.repulsion();
};
Tree.prototype.level = -1;
Tree.prototype.direction = 0;
function Node(parent,tree,key,data,direction){
\t this.children = new Array();
\t this.parent = parent;
\t this.tree = tree;
\t this.key = key;
\t this.data = data;
\t if(direction){
\t \t this.direction = direction;
\t }else{
\t \t this.direction = this.parent.direction+Math.random()/10;
\t }
\t this.tree.nodes.push(this);
}
Node.prototype.addChild = function(key,data){
\t this.children.push(new Node(this,this.tree,key,data));
};
Node.prototype.draw = function(){
\t ctx.fillStyle = this.nodeColor;
\t ctx.strokeStyle = this.lineColor;
\t ctx.lineWidth = this.lineWidth;
\t if(this.key!="root"){
\t \t ctx.beginPath();
\t \t ctx.moveTo(this.x,this.y);
\t \t ctx.lineTo(this.parent.x,this.parent.y);
\t \t ctx.stroke();
\t }
\t for(var i=0;i<this.children.length;i++){
\t \t this.children[i].draw();
\t }
\t ctx.beginPath();
\t ctx.arc(this.x,this.y,this.size,0,2*Math.PI,false);
\t ctx.fill();
\t ctx.stroke();
}
Node.prototype.repulsion = function(){
\t if(this.key!="root"){
\t \t var force = {
\t \t \t x: 0,
\t \t \t y: 0
\t \t };
\t \t var pos = {
\t \t \t x: this.x,
\t \t \t y: this.y
\t \t };
\t \t var nodes = this.tree.nodes;
\t \t for(var i=0;i<nodes.length;i++){
\t \t \t var node = nodes[i];
\t \t \t if(node!=this){
\t \t \t \t var distance = Math.sqrt(Math.pow(pos.x-node.x,2)+Math.pow(pos.y-node.y,2));
\t \t \t \t var direction = Math.atan2((pos.y-node.y),(pos.x-node.x));
\t \t \t \t var magnitude = 1/Math.pow(distance,2)/(node.level+1);
\t \t \t \t force.x += Math.cos(direction)*magnitude;
\t \t \t \t force.y += Math.sin(direction)*magnitude;
\t \t \t }
\t \t }
\t \t force.x *= this.tree.repulsionFactor;
\t \t force.y *= this.tree.repulsionFactor;
\t \t var newPos = {
\t \t \t x: pos.x+force.x,
\t \t \t y: pos.y+force.y
\t \t };
\t \t var newDirection = Math.atan2((newPos.y-this.parent.y),(newPos.x-this.parent.x));
\t \t this.direction = newDirection;
\t }else{
\t \t this.direction = this.parent.direction;
\t }
\t for(var i=0;i<this.children.length;i++){
\t \t this.children[i].repulsion();
\t }
};
Object.defineProperty(Node.prototype,"x",{
\t get: function x(){
\t \t if(this.key=="root"){
\t \t \t return this.parent.x;
\t \t }
\t \t return Math.cos(this.direction)*this.linkLength+this.parent.x;
\t }
});
Object.defineProperty(Node.prototype,"y",{
\t get: function y(){
\t \t if(this.key=="root"){
\t \t \t return this.parent.y;
\t \t }
\t \t return Math.sin(this.direction)*this.linkLength+this.parent.y;
\t }
});
Object.defineProperty(Node.prototype,"level",{
\t get: function level(){
\t \t return this.parent.level+1;
\t }
});
Object.defineProperty(Node.prototype,"nodeColor",{
\t get: function nodeColor(){
\t \t return this.parent.nodeColor;
\t }
});
Object.defineProperty(Node.prototype,"lineColor",{
\t get: function lineColor(){
\t \t return this.parent.lineColor;
\t }
});
Object.defineProperty(Node.prototype,"lineWidth",{
\t get: function lineWidth(){
\t \t return this.parent.lineWidth;
\t }
});
Object.defineProperty(Node.prototype,"size",{
\t get: function size(){
\t \t return this.parent.size*0.8;
\t }
});
Object.defineProperty(Node.prototype,"linkLength",{
\t get: function linkLength(){
\t \t return this.parent.linkLength*0.8;
\t }
});
<!DOCTYPE html>
<html>
\t <head>
\t \t <meta charset="utf-8">
\t \t <title>Node Tree</title>
\t </head>
\t <body onload="setup()">
\t \t <canvas id="canvas"></canvas>
\t </body>
</html>
這是一個有趣的問題要解決,我會盡量弄糟算法。我找到了一個討論這個問題的文檔。 http://www.cs.unc.edu/techreports/89-034.pdf如果我成功實施它,我會來發布答案。 –
@RomainIsnel非常感謝:)我實際上偶然發現了這篇文章,但我認爲我的英語和編碼技能對於這種東西來說很低......我期待着您的解決方案。 –
我可能需要一兩天的時間才能實現這個解決方案,因爲我從那以後做了一些工作,但我一定會試一試。 –