2013-05-01 8 views
6

Widziałem rozwiązania do tworzenia wielu układów sił na jednej stronie, gdzie każdy układ jest zawarty we własnym SVG; jednak nie udało mi się znaleźć pomocy dotyczącej uwzględniania wielu układów sił w jednym pliku SVG. Każdy układ ma przypisane własne dane.D3: Wiele układów sił w jednym SVG?

Przykład tego, co obecnie robię, można znaleźć pod adresem http://jsfiddle.net/connorgr/SRAJa/. Dołączyłem kluczową część poniższego kodu. Wynik końcowy wygląda bardzo podobnie, ponieważ układ siły nigdy nie został aktywowany (lub usunięty) dla wszystkich, oprócz danych ostatniego węzła/łącza. Czy istnieje sposób, aby temu zapobiec?

Nie mogę scalić danych razem i użyć tylko jednego układu ze względu na przypadek użycia wizualizacji, którą buduję.

/** 
    * Creates a force layout in svgObj for each element in graphs 
    * (svg) svgObj - The SVG to include the force layouts in 
    * (json) graphs - An array of {"nodes":[...],"links":[...]} objects 
    */ 
function generateMultiForce(svgObj, graphs) { 
    for(var i=0; i < graphs.length; i++) { 
    var graph = graphs[i]; 

    var graphArea = svgObj.append("g"); 

    var force = d3.layout.force() 
     .charge(-200) 
     .linkDistance(45) 
     .size([width, height]) 
     .nodes(graph.nodes) 
     .links(graph.links) 
     .start(); 

    var link = graphArea.selectAll(".link") 
     .data(graph.links) 
     .enter().append("line") 
      .attr("class", "link"); 

    var nodeGroup = graphArea.selectAll("g") 
     .data(graph.nodes) 
     .enter().append("g") 
     .call(force.drag); 

    var node = nodeGroup.append("circle") 
     .attr("class", "node") 
     .attr("r", 5) 
     .style("fill", function(d) { 
      return color(d.group); }); 

    var text = nodeGroup.append("text") 
     .attr("x", 8) 
     .attr("y", ".31em") 
     .text(function(d) { return d.name; }); 

    force.on("tick", function() { 
     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; }); 

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

Wydaje mi się, że wydarzenie "tick" jest (w pewnym sensie) globalne. Oznacza to, że możesz mieć tylko jeden moduł obsługi dla zdarzenia "tick", więc ostatni, który instalujesz zostanie wywołany. –

+0

Zrobiłem krótkie zanurzenie w kodzie źródłowym D3 i wydaje się, że to właśnie się dzieje. Bummer. –

Odpowiedz

2

Można napisać funkcję generateMultiForce jako plugin jQuery - to wydaje się zachować wykresy niezależny i zastosować układ sił zarówno:

var width = 600, 
    height = 600; 

var color = d3.scale.category20(); 

var data = [ 
    { 
    "nodes": [ 
     {"name": "Hello"}, 
     {"name": "World"} 
    ], 
    "links": [ 
     {"source": 0, "target": 1, "value": 0.5} 
    ] 
    }, 
    { 
    "nodes": [ 
     {"name": "Zero"}, 
     {"name": "One"}, 
     {"name": "Two"} 
    ], 
    "links": [ 
     {"source": 0, "target": 1, "value": 0.25}, 
     {"source": 1, "target": 2, "value": 0.5}, 
     {"source": 2, "target": 0, "value": 0.25} 
    ] 
    } 
]; 

(function($) { 
    $.fn.generateMultiForce = function(svgObj) { 

     return this.each(function() { 
      var graph = this; 
      var graphArea = svgObj.append("g"); 

      var force = d3.layout.force() 
       .charge(-200) 
       .linkDistance(45) 
       .size([width, height]) 
       .nodes(graph.nodes) 
       .links(graph.links) 
       .start(); 

      var link = graphArea.selectAll(".link") 
       .data(graph.links) 
       .enter().append("line") 
       .attr("class", "link"); 

      var nodeGroup = graphArea.selectAll("g") 
       .data(graph.nodes) 
       .enter().append("g") 
       .call(force.drag); 

      var node = nodeGroup.append("circle") 
       .attr("class", "node") 
       .attr("r", 5) 
       .style("fill", function(d) { 
        return color(d.group); }); 

      var text = nodeGroup.append("text") 
       .attr("x", 8) 
       .attr("y", ".31em") 
       .text(function(d) { return d.name; }); 

      force.on("tick", function() { 
       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; }); 

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

var svgTest = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height); 

$(data).generateMultiForce(svgTest); 
6

następujące podejście ogranicza siłę, węzła i łącza danych do ich odpowiednich układów skierowanych na siłę. W ten sposób możesz dodać tyle układów, ile chcesz, do tego samego SVG bez powodowania interferencji między węzłami. Każdy układ może być formatowany indywidualnie. Jeśli w końcu chcesz, aby układy wpływały na siebie nawzajem, możesz edytować ich odpowiednie funkcje zaznaczania.

function layout1(inputNodes, inputLinks) { 
    var force = d3.layout.force(); 
    var nodes = force.nodes(); 
    var links = force.links(); 

    var update = function() { 
     //append nodes and links from data 

     force.on("tick",function(e){ 
     //tick movement 

     } 
    } 
    for(var i=0; i<inputNodes.length; i++){ 
     nodes.push(inputNodes[i]); 
    } 
    for(var i=0; i<inputLinks.length; i++){ 
     links.push(inputLinks[i]); 
    } 
    update(); 
} 

Teraz drugi układ siły kierowane mogą mieć identyczną konstrukcję i te same nazwy zmiennych, a także:

function layout2(inputNodes, inputLinks) { 
    var force = d3.layout.force(); 
    var nodes = force.nodes(); 
    var links = force.links(); 

    var update = function() { 
     //append nodes and links from data 

     force.on("tick",function(e){ 
     //tick movement 

     } 
    } 
    for(var i=0; i<inputNodes.length; i++){ 
     nodes.push(inputNodes[i]); 
    } 
    for(var i=0; i<inputLinks.length; i++){ 
     links.push(inputLinks[i]); 
    } 
    update(); 
} 

Wreszcie wystąpienia z jakiegokolwiek danych:

var layout1 = new layout1(inputNodes, inputLinks); 
var layout2 = new layout2(inputNodes, inputLinks); 

Metoda ta można zaadaptować do tworzenia wielu układów w locie. Mam nadzieję, że jest blisko tego, czego szukasz.