2015-11-06 7 views
0

У меня есть скрипка - http://jsfiddle.net/npmarkunda/pqobc0zv/Как я группа узлы в силе ориентированного граф

Как показывают узлы в соответствии с группой, которой они принадлежат.

Что такое «группа»? Почему у границ есть как «источник», так и «цель» - и каковы эти значения? Почему ссылки имеют «ценность»? Разве ссылки не просто невзвешенные края? У меня возникли проблемы с пониманием структуры хранения данных JSON.

А также как визуализировать текст как текст, а не как SVG.

var graph; 
 
    function myGraph() { 
 

 
     // Add and remove elements on the graph object 
 
     this.addNode = function (id) { 
 
      nodes.push({"id": id}); 
 
      update(); 
 
     }; 
 

 
     this.removeNode = function (id) { 
 
      var i = 0; 
 
      var n = findNode(id); 
 
      while (i < links.length) { 
 
       if ((links[i]['source'] == n) || (links[i]['target'] == n)) { 
 
        links.splice(i, 1); 
 
       } 
 
       else i++; 
 
      } 
 
      nodes.splice(findNodeIndex(id), 1); 
 
      update(); 
 
     }; 
 

 
     this.removeLink = function (source, target) { 
 
      for (var i = 0; i < links.length; i++) { 
 
       if (links[i].source.id == source && links[i].target.id == target) { 
 
        links.splice(i, 1); 
 
        break; 
 
       } 
 
      } 
 
      update(); 
 
     }; 
 

 
     this.removeallLinks = function() { 
 
      links.splice(0, links.length); 
 
      update(); 
 
     }; 
 

 
     this.removeAllNodes = function() { 
 
      nodes.splice(0, links.length); 
 
      update(); 
 
     }; 
 

 
     this.addLink = function (source, target, value) { 
 
      links.push({"source": findNode(source), "target": findNode(target), "value": value}); 
 
      update(); 
 
     }; 
 

 
     var findNode = function (id) { 
 
      for (var i in nodes) { 
 
       if (nodes[i]["id"] === id) return nodes[i]; 
 
      } 
 
      ; 
 
     }; 
 

 
     var findNodeIndex = function (id) { 
 
      for (var i = 0; i < nodes.length; i++) { 
 
       if (nodes[i].id == id) { 
 
        return i; 
 
       } 
 
      } 
 
      ; 
 
     }; 
 

 
     // set up the D3 visualisation in the specified element 
 
     var w = 600, 
 
       h = 650; 
 

 
     var color = d3.scale.category10(); 
 

 
     var vis = d3.select("body") 
 
       .append("svg:svg") 
 
       .attr("width", w) 
 
       .attr("height", h) 
 
       .attr("id", "svg") 
 
       .attr("pointer-events", "all") 
 
       .attr("viewBox", "0 0 " + w + " " + h) 
 
       .attr("perserveAspectRatio", "xMinYMid") 
 
       .append('svg:g'); 
 

 
     var force = d3.layout.force(); 
 

 
     var nodes = force.nodes(), 
 
       links = force.links(); 
 

 
     var update = function() { 
 
      var link = vis.selectAll("line") 
 
        .data(links, function (d) { 
 
         return d.source.id + "-" + d.target.id; 
 
        }); 
 

 
      link.enter().append("line") 
 
        .attr("id", function (d) { 
 
         return d.source.id + "-" + d.target.id; 
 
        }) 
 
        .attr("stroke-width", function (d) { 
 
         return d.value/10; 
 
        }) 
 
        .attr("class", "link"); 
 
      link.append("title") 
 
        .text(function (d) { 
 
         return d.value; 
 
        }); 
 
      link.exit().remove(); 
 

 
      var node = vis.selectAll("g.node") 
 
        .data(nodes, function (d) { 
 
         return d.id; 
 
        }); 
 

 
      var nodeEnter = node.enter().append("g") 
 
        .attr("class", "node") 
 
        .call(force.drag); 
 

 
      nodeEnter.append("svg:circle") 
 
        .attr("r", 12) 
 
        .attr("id", function (d) { 
 
         return "Node;" + d.id; 
 
        }) 
 
        .attr("class", "nodeStrokeClass") 
 
        .attr("fill", function(d) { return color(d.id); }); 
 

 
      nodeEnter.append("svg:text") 
 
        .attr("class", "textClass") 
 
        .attr("x", 14) 
 
        .attr("y", ".31em") 
 
        .text(function (d) { 
 
         return d.id; 
 
        }); 
 

 
      node.exit().remove(); 
 

 
      force.on("tick", function() { 
 

 
       node.attr("transform", function (d) { 
 
        return "translate(" + d.x + "," + d.y + ")"; 
 
       }); 
 

 
       link.attr("x1", function (d) { 
 
        return d.source.x; 
 
       }) 
 
         .attr("y1", function (d) { 
 
          return d.source.y; 
 
         }) 
 
         .attr("x2", function (d) { 
 
          return d.target.x; 
 
         }) 
 
         .attr("y2", function (d) { 
 
          return d.target.y; 
 
         }); 
 
      }); 
 

 
      // Restart the force layout. 
 
      force 
 
        .gravity(.01) 
 
        .charge(-80000) 
 
        .friction(0) 
 
        .linkDistance(function(d) { return d.value * 10 }) 
 
        .size([w, h]) 
 
        .start(); 
 
     }; 
 

 

 
     // Make it all go 
 
     update(); 
 
    } 
 

 
    function drawGraph() { 
 

 
     graph = new myGraph("#svgdiv"); 
 
     graph.addNode('Sophia'); 
 
     graph.addNode('Daniel'); 
 
     graph.addNode('Ryan'); 
 
     graph.addNode('Lila'); 
 
     graph.addNode('Suzie'); 
 
     graph.addNode('Riley'); 
 
     graph.addNode('Grace'); 
 
     graph.addNode('Dylan'); 
 
     graph.addNode('Mason'); 
 
     graph.addNode('Emma'); 
 
     graph.addNode('Alex'); 
 
     graph.addLink('Alex', 'Ryan', '20'); 
 
     graph.addLink('Sophia', 'Ryan', '20'); 
 
     graph.addLink('Daniel', 'Ryan', '20'); 
 
     graph.addLink('Ryan', 'Lila', '30'); 
 
     graph.addLink('Lila', 'Suzie', '20'); 
 
     graph.addLink('Suzie', 'Riley', '10'); 
 
     graph.addLink('Suzie', 'Grace', '30'); 
 
     graph.addLink('Grace', 'Dylan', '10'); 
 
     graph.addLink('Dylan', 'Mason', '20'); 
 
     graph.addLink('Dylan', 'Emma', '20'); 
 
     graph.addLink('Emma', 'Mason', '10'); 
 
     graph.addLink('Grace', 'Daniel', '5'); 
 
     graph.addLink('Alex', 'Mason', '35'); 
 
     keepNodesOnTop(); 
 

 
     // callback for the changes in the network 
 
     var step = -1; 
 
     function nextval() 
 
     { 
 
      step++; 
 
      return 2000 + (1500*step); // initial time, wait time 
 
     } 
 
    
 
    } 
 

 
    drawGraph(); 
 

 
    // because of the way the network is created, nodes are created first, and links second, 
 
    // so the lines were on top of the nodes, this just reorders the DOM to put the svg:g on top 
 
    function keepNodesOnTop() { 
 
     $(".nodeStrokeClass").each(function(index) { 
 
      var gnode = this.parentNode; 
 
      gnode.parentNode.appendChild(gnode); 
 
     }); 
 
    } 
 
    function addNodes() { 
 
     d3.select("svg") 
 
       .remove(); 
 
     drawGraph(); 
 
    }
.link { 
 
      stroke: #999; 
 
      stroke-width: 1px; 
 
     } 
 

 
     .node { 
 
      stroke: #999; 
 
      stroke-width: 1px; 
 
     } 
 
     .textClass { 
 
      stroke: #555; 
 
      font-family: "Lucida Grande", "Droid Sans", Arial, Helvetica, sans-serif; 
 
      font-weight: normal; 
 
      font-size: 14px; 
 
     }
<script src="http://d3js.org/d3.v3.min.js"></script> 
 
<button onclick="addNodes()">Restart Animation</button>

ответ

0

D3js имеет linkDistance вариант, который позволяет устанавливать расстояние между двумя узлами в зависимости от значения.

В моем примере, я должен был установить этот linkDistance(function(d) { return (d.value); })

0

D3 Force Layout : How to force a group of node to stay in a given area

Ссылки являются соединения между узлами. Исходные и целевые значения для ссылок указывают, в каком направлении должна указываться стрелка. Длина или расстояние, или любые настраиваемые атрибуты, которые вы добавляете к объекту JSON, обычно используются для указания «желаемого» linkDistance, хотя вы также можете указать вес для использования с настройкой силы тяжести.

0

Я использую отдельное forcePoints кластер моих узлов в определенных местах, она включает в себя дополнительный массив значений X и Y для каждой группы

var forcePoint = [ 
"1":{"x":"200", "y":"400"}, 
"2":{"x":"300", "y":"600"}, 
];//etc. 

Тогда я расположить узлы вокруг forcePoint присвоенного атрибута местоположения в данные. В этом случае узлы с кластером местоположения 1 составляют 200 400 на SVG, узлы с кластером местоположения 2 - 300 600. Фактический массив создается во время предыдущего шага моделирования, но вы получаете идею.

var position = d3.forceSimulation(nodes) 
.force('x', d3.forceX((d)=>forcePoint[d.location][0].cx) 
.strength(0.8)) 
.force('y', d3.forceY((d)=>forcePoint[d.location][1].cy) 
.strength(0.8)) 
.force("collide", d3.forceCollide(R * 2)); 
position.nodes(graph.cells).on('tick', function() { 
    nodes.attr('transform', (d)=>{ 
    return 'translate(' + (d.x) + ',' + (d.y) + ')'; 
    }); 

Вы также можете связать их все с центральным узлом внутри группы.