2017-03-27 36 views
0

我需要可視化節點樹的一個項目,我的工作......的數據結構是這樣的:可視化節點樹(?斥力)

構造函數:

function Tree(x,y,node){ 
    this.root = node; 
    this.x = x; 
    this.y = y; 
} 

function Node(key,parent,data){ 
    this.children = new Array(); 
    this.key = key; 
    this.parent = parent; 
    this.data = data; 
    this.direction = ???; 
} 

每個children陣列可以容納更多的節點,讓我得到一個樹形結構...的Treexy值被用來獲取的位置,那裏的樹的根應該繪製,而Node STO的direction財產資源的角度,其中Node應視parent角度可以得出....

所以現在我的問題:我需要爲Tree.prototype功能draw(context)可以使用給定的畫布背景繪製Tree

這裏是這樣的Tree應該怎麼看起來像一個例子:

其實我有這樣的算法可能是如何工作的想法,但我不能把它轉換成代碼..這裏是:

10每個節點都推開其他節點。力量取決於節點和它們的級別之間的距離(根級別爲0,它是兒童級別1等等)。

我想,那幾個itereations後一個好看的樹將被創建...

一個巨大的感謝您已經萬一你盡力幫助我,甚至試圖創建這樣的算法.. 。

編輯:

這裏是我試過到目前爲止:

Tree.prototype.repulsion = function(){ 
    if(this.root){ 
     this.root.repulsion(); 
    } 
}; 
Node.prototype.repulsion = function(){ 
    var force = { 
     x: 0, 
     y: 0 
    }; 
    var pos = { 
     x: this.x, 
     y: this.y 
    }; 
    var oldDirection = this.direction; 
    for(var i=0;i<nodes.length;i++){ 
     var node = nodes[i]; 
     if(node!=this){ 
      var distance = Math.sqrt(Math.pow(pos.x-node.x,2)+Math.pow(pos.y-node.y,2)); 
      var direction = Math.atan((this.y-node.y)/(this.x-node.x)); 
      var magnitude = 1/Math.pow(distance,2); 
      if(pos.x<node.x){ 
       direction *= -1; 
       force.x -= Math.cos(direction)*magnitude; 
      }else{ 
       force.x += Math.cos(direction)*magnitude; 
      } 
      force.y += Math.sin(direction)*magnitude; 
     } 
    } 
    force.x *= repulsionFactor; 
    force.y *= repulsionFactor; 
    var newPos = { 
     x: pos.x+force.x, 
     y: pos.y+force.y 
    }; 
    var newDirection = Math.atan((newPos.y-this.parent.y)/(newPos.x-this.parent.x)); 
    if(force.x<0){ 
     newDirection += Math.PI; 
    } 
    this.direction = newDirection; 
    this.direction %= 2*Math.PI; 
    for(var i=0;i<this.children.length;i++){ 
     this.children[i].repulsion(); 
    } 
}; 
+1

這是一個有趣的問題要解決,我會盡量弄糟算法。我找到了一個討論這個問題的文檔。 http://www.cs.unc.edu/techreports/89-034.pdf如果我成功實施它,我會來發布答案。 –

+0

@RomainIsnel非常感謝:)我實際上偶然發現了這篇文章,但我認爲我的英語和編碼技能對於這種東西來說很低......我期待着您的解決方案。 –

+0

我可能需要一兩天的時間才能實現這個解決方案,因爲我從那以後做了一些工作,但我一定會試一試。 –

回答

2

其實我有我自己的想法而努力!

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>

出於某種原因,樹保持有時候不是我創造它展現一棵樹每一次旋轉,並且還......但希望你可以在此展開!

+1

如果你通過增加一個額外的'0'來增加初始'repulsionFactor',那麼你可以肯定它展開了。爲了對抗擺動和旋轉,你可以使用類似'tree.repulsionFactor * = 0.99;'的方式逐漸降低'main'函數的排斥率。如果您稍後需要添加節點,則可以將'repulsionFactor'重置爲較高值,然後再次減慢。 –

+0

這真的很棒!這就是你想要的@安頓鮑爾邁爾。我會說很好的工作。 –

+0

@EmilS.Jørgensen謝謝,看起來像幫助:) –

2

東西鏈接呢?

我使用後代的數量來推動節點離開它們各自的根。這與基於水平的一些角度修改相結合使得獲得重疊筆記的可能性很小,但並非不可能。

var Branch = (function() { 
 
    function Branch(parent, key, data) { 
 
    if (parent === void 0) { 
 
     parent = null; 
 
    } 
 
    if (key === void 0) { 
 
     key = "test"; 
 
    } 
 
    if (data === void 0) { 
 
     data = "null"; 
 
    } 
 
    this.parent = parent; 
 
    this.key = key; 
 
    this.data = data; 
 
    this.children = []; 
 
    this.pos = { 
 
     x: 0, 
 
     y: 0 
 
    }; 
 
    if (this.parent != null) { 
 
     this.parent.children.push(this); 
 
    } 
 
    } 
 
    Branch.prototype.level = function() { 
 
    if (this.parent != null) { 
 
     return this.parent.level() + 1; 
 
    } 
 
    return 0; 
 
    }; 
 
    Branch.prototype.descendants = function() { 
 
    var count = 0; 
 

 
    function descent(target) { 
 
     count += target.children.length; 
 
     for (var i = 0; i < target.children.length; i++) { 
 
     descent(target.children[i]); 
 
     } 
 
    } 
 
    descent(this); 
 
    return count; 
 
    }; 
 
    return Branch; 
 
}()); 
 
var Tree = (function() { 
 
    function Tree(pos, node) { 
 
    this.pos = pos; 
 
    this.node = node; 
 
    this.node.pos = this.pos; 
 
    } 
 
    Tree.prototype.render = function(canvas, padding, nodeSize, longpath, showText) { 
 
    if (padding === void 0) { 
 
     padding = 100; 
 
    } 
 
    if (nodeSize === void 0) { 
 
     nodeSize = 10; 
 
    } 
 
    if (longpath === void 0) { 
 
     longpath = 4; 
 
    } 
 
    if (showText === void 0) { 
 
     showText = true; 
 
    } 
 
    var ctx = canvas.getContext("2d"); 
 
    //flatten 
 
    var flattened = []; 
 

 
    function getLevel(branch) { 
 
     flattened.push(branch); 
 
     for (var index = 0; index < branch.children.length; index++) { 
 
     getLevel(branch.children[index]); 
 
     } 
 
    } 
 
    getLevel(this.node); 
 
    //disperse 
 
    function disperseLevel(branch) { 
 
     if (branch.parent != null) { 
 
     branch.pos.x = branch.parent.pos.x; 
 
     branch.pos.y = branch.parent.pos.y; 
 
     var angle = (((360/(branch.parent.children.length + branch.level())) * branch.parent.children.indexOf(branch)) + (branch.level() * 10)) * Math.PI/180; 
 
     branch.pos.x += Math.cos(angle) * (padding * (branch.descendants()/longpath + 1)); 
 
     branch.pos.y += Math.sin(angle) * (padding * (branch.descendants()/longpath + 1)); 
 
     } 
 
     for (var index = 0; index < branch.children.length; index++) { 
 
     disperseLevel(branch.children[index]); 
 
     } 
 
    } 
 
    disperseLevel(this.node); 
 
    //draw 
 
    for (var index = 0; index < flattened.length; index++) { 
 
     var branch = flattened[index]; 
 
     ctx.fillStyle = "rgba(" + (255 - ((branch.level()) * 48) % 255).toString() + ',0,0,1)'; 
 
     ctx.strokeStyle = "rgba(" + (255 - ((branch.level()) * 48) % 255).toString() + ',0,0,1)'; 
 
     ctx.fillRect(branch.pos.x - (0.5 * nodeSize), branch.pos.y - (0.5 * nodeSize), nodeSize, nodeSize); 
 
     if (branch.parent != null) { 
 
     ctx.beginPath(); 
 
     ctx.moveTo(branch.pos.x, branch.pos.y); 
 
     ctx.lineTo(branch.parent.pos.x, branch.parent.pos.y); 
 
     ctx.closePath(); 
 
     ctx.stroke(); 
 
     } 
 
     if (showText === true) { 
 
     ctx.strokeText(branch.key + ': ' + branch.data, branch.pos.x, branch.pos.y); 
 
     } 
 
    } 
 
    }; 
 
    return Tree; 
 
}()); 
 
//TEST 
 
//setup canvas 
 
var c = document.body.appendChild(document.createElement("canvas")); 
 
var ctx = c.getContext("2d"); 
 
c.width = 3000; 
 
c.height = 3000; 
 
ctx.textAlign = "center"; 
 
ctx.font = "14px Arial"; 
 
//create nodes 
 
var key = 1; 
 
var nodes = [ 
 
    new Branch(null, (key++).toString(), "ROOT") 
 
]; 
 
//create tree 
 
var tree = new Tree({ 
 
    x: 1000, 
 
    y: 1000 
 
}, nodes[0]); 
 
//render tree using canvas 
 
setInterval(function() { 
 
    if (nodes.length > 50) { 
 
    return true; 
 
    } 
 
    ctx.clearRect(0, 0, c.width, c.height); 
 
    nodes.push(new Branch(nodes[Math.floor(Math.random() * (nodes.length - 1))], (key++).toString())); 
 
    tree.render(c, 100, 10, 4, true); 
 
}, 100);

+0

這是一種作品,但是因爲我的目標是讓它看起來令人愉快,所以推動節點越來越遠並不是一個真正的選擇:/我真的很感謝你的工作,但我認爲這不是,我要去哪裏與...一起...代碼背後有一些有趣的想法:) –