2012-03-05 12 views
34

Sono nuovo in D3 e ho difficoltà a impostare i limiti per il layout della forza diretta. Sono riuscito a mettere insieme (dagli esempi) ciò che vorrei, ma ho bisogno che il grafico sia contenuto. Nella funzione tick, una trasformazione/traduzione visualizzerà correttamente il mio grafico, ma quando uso cx e cy con Math.max/min (Vedi codice commentato), i nodi sono bloccati nell'angolo in alto a sinistra mentre le linee sono contenute correttamente .Layout diretto a forza D3 con riquadro di delimitazione

Ecco cosa ho di seguito ... cosa sto facendo male ??

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

Ho giocato con alcuni parametri e ha deciso che, a meno che qualcuno ha la soluzione, mi limiterò a usare un sacco di .gravity(). Se il grafico è molto grande, rappresenta comunque un problema, ma per il resto dovrebbe contenere i nodi. –

risposta

59

C'è un bounding box example nel mio talk on force layouts. La posizione Verlet integration consente di definire i vincoli geometrici (come bounding box e collision detection) all'interno del listener di eventi "tick"; semplicemente spostare i nodi per rispettare il vincolo e la simulazione si adatterà di conseguenza.

Detto questo, la gravità è sicuramente un modo più flessibile per affrontare questo problema, poiché consente agli utenti di trascinare temporaneamente il grafico all'esterno del riquadro di delimitazione e quindi il grafico si ripristinerà. A seconda della dimensione del grafico e della dimensione dell'area visualizzata, è necessario sperimentare diversi punti di forza relativi di gravità e carica (repulsione) per far sì che il grafico si adatti.

+17

La chiave è aggiungere 'node.attr (" cx ", funzione (d) {return dx = Math.max (r, Math.min (larghezza - r, dx))}) .attr (" cy " , function (d) {return dy = Math.max (r, Math.min (height - r, dy));}); ' Se usi' path' invece di 'line', dovrai aggiungere il limite controlla per 'attr ('d', function() {...});' '' pure. – Limin

0

Il codice commentato funziona su un nodo che è, dalla definizione, un elemento svg g (rouping) e non utilizza gli attributi cx/cy. Selezionare l'elemento cerchio all'interno del nodo di rendere questi attributi prendono vita:

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)); });