[英]How to move a node from one simulation to another
我正在尝试将节点从一个forceSimulation
移动到另一个,并且我希望节点离开一个集群并转移到另一个集群。 然而,似乎一旦模拟进入稳定模式,力就会停止施加。
这是代码笔:迁移节点
和一个片段:
const width = 600; const height = 200; var nodes; var simulations; function loaded() { d3.select("svg").attr("width", width).attr("height", height) test(); } function test() { nodes = []; nodes[0] = d3.range(50).map(function() { return { radius: 4, color: "blue" }; }) nodes[1] = d3.range(50).map(function() { return { radius: 4, color: "red" }; }) simulations = [null, null]; update(0); update(1); setTimeout(startMigration, 1000); } function update(index) { simulations[index] = d3.forceSimulation(nodes[index]).force('charge', d3.forceManyBody().strength(10)).force('x', d3.forceX().x(function(d) { return width * (index + 1) / 3; })).force('y', d3.forceY().y(function(d) { return height / 2; })).force('collision', d3.forceCollide().radius(function(d) { return d.radius; })).on('tick', ticked); function ticked() { var u = d3.select('svg').selectAll('.circle' + index).data(nodes[index]).join('circle').attr('class', 'circle' + index).attr('r', function(d) { return d.radius; }).style('fill', function(d) { //return colorScale(d.category); return d.color; }).attr('cx', function(d) { return dx; }).attr('cy', function(d) { return dy; }); } } function startMigration() { setInterval(function() { if (nodes[1].length > 10) { nodes[0].push(nodes[1].pop()); d3.select('svg').selectAll('.circle0').data(nodes[0]); simulations[0].nodes(nodes[0]); d3.select('svg').selectAll('.circle1').data(nodes[1]); simulations[1].nodes(nodes[1]); } }, 250); }
<html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body onload="loaded()"> <div id='layout'> <div id='container'> <svg id="graph" /> </div> </div> </body> </html>
nodes
是两个数据集的数组,而simulations
是两个模拟的数组。
这是我创建forceSimulation
的地方:
function update(index) {
simulations[index] = d3.forceSimulation(nodes[index])
.force('charge', d3.forceManyBody().strength(10))
.force('x', d3.forceX().x(function(d) {
return width * (index + 1) / 3;
}))
.force('y', d3.forceY().y(function(d) {
return height/2;
}))
.force('collision', d3.forceCollide().radius(function(d) {
return d.radius;
}))
.on('tick', ticked);
function ticked() {
var u = d3.select('svg')
.selectAll('.circle' + index)
.data(nodes[index])
.join('circle')
.attr('class', 'circle' + index)
.attr('r', function(d) {
return d.radius;
})
.style('fill', function(d) {
//return colorScale(d.category);
return d.color;
})
.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
});
}
}
这是在模拟开始将节点从一个模拟移动到另一个模拟后 1000 毫秒开始定期调用的代码:
nodes[0].push(nodes[1].pop());
d3.select('svg')
.selectAll('.circle0')
.data(nodes[0]);
simulations[0].nodes(nodes[0]);
d3.select('svg')
.selectAll('.circle1')
.data(nodes[1]);
simulations[1].nodes(nodes[1]);
我希望的效果是节点会离开红色集群并漂移加入蓝色集群。 为什么他们会减速并停下来? 初始加载后等待的时间越长,问题就越严重。 我觉得我不理解有关forceSimulation
的一些基本知识。 我本以为他们会尽可能接近他们所属的模拟的( forceX
, forceY
)。
第二个问题是上面代码片段中的所有步骤是否都是必要的。 我是否需要将数据重新分配给两个选择,并将节点重新分配给两个模拟?
在使用simulation.alpha(n).restart()
进行拖动操作期间,通过用户交互进行的大量力模拟“重新加热”模拟。 例如,请参阅此可观察对象。
您可以在setMigration
function 中无需用户交互来执行此操作,以保持模拟 animation 更新(可以说保持“温暖”),直到足够的圆圈从“红色”移动到“蓝色”。 要添加的两行是:
simulations[0].alpha(0.15).restart();
simulations[1].alpha(0.15).restart();
如果增加 0.15(最多 1),您会发现两种模拟在迁移发生时都更“容易兴奋”。 0.15 对我来说似乎是一种愉快的视觉体验。 这似乎在没有.restart()
的情况下也能正常工作,因此您很可能会根据等待启动setMigration
的时间来使用它。
对于你的第二个问题 - 因为每个模拟都被初始化为nodes[0]
和nodes[1]
那么d3.select('svg').selectAll('.circleN')
可以被注释掉,因为你已经改变了数组内容nodes[0].push(nodes[1].pop())
。
下面的工作示例:
const width = 600; const height = 200; var nodes; var simulations; function loaded() { d3.select("svg").attr("width", width).attr("height", height) test(); } function test() { nodes = []; nodes[0] = d3.range(50).map(function() { return { radius: 4, color: "blue" }; }) nodes[1] = d3.range(50).map(function() { return { radius: 4, color: "red" }; }) simulations = [null, null]; update(0); update(1); setTimeout(startMigration, 1000); } function update(index) { simulations[index] = d3.forceSimulation(nodes[index]).force('charge', d3.forceManyBody().strength(10)).force('x', d3.forceX().x(function(d) { return width * (index + 1) / 3; })).force('y', d3.forceY().y(function(d) { return height / 2; })).force('collision', d3.forceCollide().radius(function(d) { return d.radius; })).on('tick', ticked); function ticked() { var u = d3.select('svg').selectAll('.circle' + index).data(nodes[index]).join('circle').attr('class', 'circle' + index).attr('r', function(d) { return d.radius; }).style('fill', function(d) { //return colorScale(d.category); return d.color; }).attr('cx', function(d) { return dx; }).attr('cy', function(d) { return dy; }); } } function startMigration() { setInterval(function() { if (nodes[1].length > 10) { nodes[0].push(nodes[1].pop()); //d3.select('svg') //.selectAll('.circle0') //.data(nodes[0]); simulations[0].nodes(nodes[0]); //d3.select('svg') //.selectAll('.circle1') //.data(nodes[1]); simulations[1].nodes(nodes[1]); // reheat the simulations simulations[0].alpha(0.15).restart(); simulations[1].alpha(0.15).restart(); } }, 250); }
<html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <script src="https://d3js.org/d3.v5.min.js"></script> </head> <body onload="loaded()"> <div id='layout'> <div id='container'> <svg id="graph" /> </div> </div> </body> </html>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.