2016-08-26 1 views
1

J'essaie de reproduire this example ("Static Force Layout" by Mike Bostock) dans D3 v4.Disposition de la force statique dans D3 v4

J'ai combiné le code de Mike avec ce que j'ai appris en créant un graphique de force dynamique (qui peut être trouvé here). Je pensais (peut-être à tort) qu'un graphique de force statique serait plus facile, mais je ne comprends pas complètement la logique du tutoriel de Mike et je ne peux pas le «traduire en langage v4».

Dans l'exemple de Mike à chaque fois que la page est mise à jour les noeuds prennent une position différente. Ma compréhension est que l'opération de chaque tick déplace chaque nœud au hasard. Par conséquent, la position est déterminée (de manière aléatoire) par cette partie du code:

// Use a timeout to allow the rest of the page to load first. 
setTimeout(function() { 

// Run the layout a fixed number of times. 
// The ideal number of times scales with graph complexity. 
// Of course, don't run too long—you'll hang the page! 
force.start(); 
for (var i = n * n; i > 0; --i) force.tick(); 
force.stop(); 

svg.selectAll("line") 
    .data(links) 
    .enter().append("line") 
    .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; }); 

svg.selectAll("circle") 
    .data(nodes) 
    .enter().append("circle") 
    .attr("cx", function(d) { return d.x; }) 
    .attr("cy", function(d) { return d.y; }) 
    .attr("r", 4.5); 

loading.remove(); 
}, 10); 

Où n est défini arbitrairement.

Voilà comment je traduis cette partie:

setTimeout(function() { 

var simulation = d3.forceSimulation() 
     .force('link', d3.forceLink().id(function (d) { return d.ID; })) 
     .force('charge', d3.forceManyBody()) 
     .force('center', d3.forceCenter(width/2, height/2)); 

var n = 1000; 
//I thought that since I have many nodes and edges in my graph I should use a highgher n. However, wheter n=10 or 1000 nothing changes in the final result 

for (var i = n * n; i > 0; --i) simulation.tick(); 

simulation 
    .nodes(data.nodes) 
    .on('tick', ticked); 

simulation.force('link') 
    .links(data.edges) 
    .distance(distance); 

function ticked() { 
    d3.selectAll('circle') 
     .attr('cx', function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); }) 
     .attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); }); 

    d3.selectAll('line') 
     .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; });   
} 
//Strange fact: I need to have both this tick function and the position attributes for each node and edge for the visualization to show something. 

simulation.stop(); 

var link = graph.append("g") 
    .attr('class', 'links') 
    .selectAll("line") 
    .data(data.edges) 
    .enter() 
    .append("line") 
    .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 node = graph.append("g") 
    .attr('class', 'nodes') 
    .selectAll("circle") 
    .data(data.nodes) 
    .enter() 
    .append("circle") 
    .attr("r", radius) 
    .attr('fill', 'red') 
    .attr('cx', function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); }) 
    .attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); }); 

loading.remove(); 
}, 10); 

Toute idée de ce que je fais mal?

Répondre

0

Mise à jour 2016-10-18 Les trucs ci-dessous à propos de la mise en œuvre d'une fonction de tique non libéré pour semble totalement erronée de ce que je peux dire après avoir joué pour un autre jour ou. Je pensais plutôt que de simplement supprimer le post que je mettrais à jour avec ma nouvelle compréhension juste pour la postérité. Pour être clair, cochez, d'après ce que je peux dire, est en effet nécessaire pour que la simulation fasse n'importe quoi avec vos objets.

Je travaille à travers un problème similaire en ce moment et ne possède pas une solution, mais a remarqué un peu de code qui peut être utile Reconsidérer:

De l'on("tick", f(x)) doc:

Remarque les événements tick ne sont pas distribués lorsque simulation.tick est appelée manuellement; les événements sont uniquement distribués par le temporisateur interne et sont destinés au rendu interactif de la simulation. Pour affecter la simulation, enregistrez les forces au lieu de modifier les positions ou les vitesses des nœuds à l'intérieur d'un écouteur d'événement tick.

Tout cela me porte à croire que le corps du code suivant est déplacé, peut-être même incorrect:

function ticked() { 
d3.selectAll('circle') 
    .attr('cx', function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); }) 
    .attr('cy', function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); }); 

d3.selectAll('line') 
    .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; });   

}

Hope that helps (je sais que c'est vieux maintenant, mais qui sait ...)

-1

vous devez arrêter et redémarrer la simulation entre le calcul de la position finale:

let _newNodes = null   
    const force = d3.forceSimulation(nodes); 

    // bind listeners first 
    force.on('tick',() => { 
     const {nodes} = this.state; 
     nodes.forEach(d => { // keep within bounds 
     d.x = Math.max(d.z, Math.min(this.props.width - d.z, d.x)); // incrementally derive bound x 
     d.y = Math.max(d.z, Math.min(this.props.height - d.z, d.y)); // incrementally derive bound y 
     }); 
     _newNodes = nodes; 
    }); 

    // register force events 
    force 
     .force('y', d3.forceY(200).strength(0.01)) 
     // all of your configs here ... 
     .stop(); 

    // calculate end state 
    for (let i = 0, n = Math.ceil(Math.log(force.alphaMin())/Math.log(1 - this.force.alphaDecay())); i < n; ++i) { 
     force.tick(); 
    } 
    // resume rendering 
    force.restart();