2012-03-05 32 views
34

Soy nuevo en D3 y tengo problemas para establecer los límites de mi diseño dirigido por fuerza. He logrado juntar (a partir de ejemplos) lo que me gustaría, pero necesito que se contenga el gráfico. En la función tick, una transformación/traducción mostrará mi gráfica correctamente, pero cuando uso cx y cy con Math.max/min (vea el código comentado), los nodos están fijados en la esquina superior izquierda mientras que las líneas están contenidas correctamente .Diseño dirigido a la fuerza D3 con cuadro delimitador

Esto es lo que tengo a continuación ... ¿qué estoy haciendo mal?

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

He jugado con algunos parámetros y he decidido que a menos que alguien tenga la solución, solo usaré gran cantidad de .gravity(). Si el gráfico es muy grande, aún supondrá un problema, pero de lo contrario debería contener los nodos bien. –

Respuesta

59

Hay una bounding box example en mi talk on force layouts. La integración Verlet de posición le permite definir restricciones geométricas (como cuadros de límite y collision detection) dentro del detector de eventos "tic"; simplemente mueva los nodos para cumplir con la restricción y la simulación se adaptará en consecuencia.

Dicho esto, la gravedad es definitivamente una forma más flexible de tratar este problema, ya que permite a los usuarios arrastrar el gráfico fuera del cuadro delimitador temporalmente y luego se recuperará el gráfico. Dependiendo del tamaño del gráfico y del tamaño del área mostrada, debe experimentar con distintas fuerzas relativas de gravedad y carga (repulsión) para que su gráfico encaje.

+17

La clave es agregar 'node.attr (" cx ", función (d) {return dx = Math.max (r, Math.min (ancho - r, dx));}) .attr (" cy " , función (d) {return dy = Math.max (r, Math.min (height - r, dy));}); ' Si usa' path' en lugar de 'line', necesitará agregar el límite compruebe 'attr ('d', function() {...});' '' también. – Limin

0

El código comentado funciona en un nodo que, desde su definición, es un elemento svg g (enrutamiento) y no opera los atributos cx/cy. Seleccione el elemento del círculo dentro del nodo para hacer que estos atributos cobren vida:

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