2012-03-05 10 views
34

Jestem nowicjuszem D3 i mam problem z ustaleniem granic dla układu sterowanego siłą. Udało mi się połączyć (z przykładów) to, co chciałbym, ale potrzebuję, aby wykres był zawarty. W funkcji tick, transform/translate wyświetli mój wykres poprawnie, ale gdy użyję cx i cy z Math.max/min (Zobacz skomentowany kod), węzły są przypięte do lewego górnego rogu rogu , podczas gdy linie są zawarte prawidłowo .Układ sterowany siłą D3 z ramką ograniczającą

Oto, co mam poniżej ... co robię źle?

var w=960, h=500, r=8, z = d3.scale.category20(); 

var color = d3.scale.category20(); 

var force = d3.layout.force() 
     .linkDistance(function(d) { return (d.value*180) }) 
     .linkStrength(function(d) { return (1/(1+d.value)) }) 
     .charge(-1000) 
     //.gravity(.08) 
     .size([w, h]); 

var vis = d3.select("#chart").append("svg:svg") 
     .attr("width", w) 
     .attr("height", h) 
     .append("svg:g") 
     .attr("transform", "translate(" + w/4 + "," + h/3 + ")"); 

vis.append("svg:rect") 
    .attr("width", w) 
    .attr("height", h) 
    .style("stroke", "#000"); 


d3.json("miserables.json", function(json) { 

     var link = vis.selectAll("line.link") 
       .data(json.links); 

     link.enter().append("svg:line") 
       .attr("class", "link") 
       .attr("x1", function(d) { return d.source.x; }) 
       .attr("y1", function(d) { return d.source.y; }) 
       .attr("x2", function(d) { return d.source.x; }) 
       .attr("y2", function(d) { return d.source.y; }) 
       .style("stroke-width", function(d) { return (1/(1+d.value))*5 }); 

     var node = vis.selectAll("g.node") 
       .data(json.nodes); 

     var nodeEnter = node.enter().append("svg:g") 
       .attr("class", "node") 
       .on("mouseover", fade(.1)) 
       .on("mouseout", fade(1)) 
       .call(force.drag); 

     nodeEnter.append("svg:circle") 
       .attr("r", r) 
       .style("fill", function(d) { return z(d.group); }) 
       .style("stroke", function(d) { return 
d3.rgb(z(d.group)).darker(); }); 

     nodeEnter.append("svg:text") 
       .attr("text-anchor", "middle") 
       .attr("dy", ".35em") 
       .text(function(d) { return d.name; }); 

     force 
     .nodes(json.nodes) 
     .links(json.links) 
     .on("tick", tick) 
     .start(); 

     function tick() { 

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

     // This contains the lines within the boundary, but the nodes are 
stuck in the top left corner 
       //node.attr("cx", function(d) { return d.x = Math.max(r, Math.min(w 
- r, d.x)); }) 
       //  .attr("cy", function(d) { return d.y = Math.max(r, Math.min(h - 
r, 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; }); 
     } 

     var linkedByIndex = {}; 

    json.links.forEach(function(d) { 
     linkedByIndex[d.source.index + "," + d.target.index] = 1; 
    }); 

     function isConnected(a, b) { 
     return linkedByIndex[a.index + "," + b.index] || 
linkedByIndex[b.index + "," + a.index] || a.index == b.index; 
    } 

     function fade(opacity) { 
     return function(d) { 
      node.style("stroke-opacity", function(o) { 
         thisOpacity = isConnected(d, o) ? 1 : opacity; 
         this.setAttribute('fill-opacity', thisOpacity); 
       return thisOpacity; 
         }); 

         link.style("stroke-opacity", opacity).style("stroke-opacity", 
function(o) { 
       return o.source === d || o.target === d ? 1 : opacity; 
       }); 
     }; 
     } 

}); 
+0

Grałem z kilkoma parametrami i zdecydowałem, że jeśli ktoś nie ma rozwiązania, po prostu użyję dużo .gravity(). Jeśli wykres jest bardzo duży, nadal będzie stanowił problem, ale poza tym powinien zawierać węzły. –

Odpowiedz

59

Jest bounding box example w moim talk on force layouts. Pozycja Integracja Verlet pozwala zdefiniować ograniczenia geometryczne (takie jak ramki ograniczające i collision detection) wewnątrz detektora zdarzeń "tick"; po prostu przesuń węzły, aby spełnić ograniczenia, a symulacja odpowiednio się dostosuje.

Powiedział, że grawitacja jest zdecydowanie bardziej elastycznym sposobem radzenia sobie z tym problemem, ponieważ umożliwia on tymczasowe przeciągnięcie wykresu poza obwiednię, a następnie wykres odzyska. W zależności od rozmiaru wykresu i wielkości wyświetlanego obszaru należy eksperymentować z różnymi względnymi mocami grawitacji i ładunku (odpychaniem), aby uzyskać odpowiedni wykres.

+17

Klucz jest dodawany 'node.attr (" cx ", funkcja (d) {return dx = Math.max (r, Math.min (szerokość - r, dx))))) .attr (" cy " , function (d) {return dy = Math.max (r, Math.min (height - r, dy));)); ' Jeśli użyjesz' path' zamiast 'line', będziesz potrzebował dodać granicę sprawdź 'attr ('d', function() {...}); również' ''. – Limin

0

Skomentowany kod działa na węźle, który jest, z twojej definicji, elementem svg g (rouping) i nie obsługuje atrybutów cx/cy. Wybierz element okręgu wewnątrz węzła, aby te atrybuty ożyły:

node.select("circle") // select the circle element in that node 
    .attr("cx", function(d) { return d.x = Math.max(r, Math.min(w - r, d.x)); }) 
    .attr("cy", function(d) { return d.y = Math.max(r, Math.min(h - r, d.y)); });