d3 v4力量作用于动态添加的节点

问题描述:

我试图将节点逐个添加到d3力仿真(在版本4中!),但有些仿真似乎并没有在仿真后被仿真演变。d3 v4力量作用于动态添加的节点


目前模拟分配一个节点,那么一个功能,ADDNODE被调用两次增加两个节点。每个都被添加到模拟中,有一个圆和一条线被渲染,并且有一个光标事件被逐个添加。

(技术上与第一和第二节点在同一时间内完成,如在第一仅设置当ADDNODE被称为第二)

然后,被点击的节点时,一个新的节点,连接到光标下的那个,应该被创建。然后这个节点应该像其他任何模拟一样在仿真的力量下进化。


然而,而一个或两个节点似乎要创建精细,后来节点似乎并不模拟下有所发展。具体来说,应该在节点之间保持一定空间的多体力似乎不起作用。


我的直觉是,被在不合适的时间模拟的勾选功能添加的节点(早期问题解决了通过添加一些simulation.stop和simulation.restart命令正在添加新节点的任何时间),但在理论上,每当添加新的物体时,应该暂停模拟。

这是在d3 v4中动态添加节点的正确实现,还是强制突出显示破坏方法的问题? This以前的答案帮助我意识到我需要合并新的条目,但部队似乎在那里工作得很好。

var w = 250; 
 
var h = 250; 
 

 
var svg = d3.select("body").append("svg"); 
 

 
svg.attr('width', w) 
 
    .attr('height', h); 
 

 
// ensures links sit beneath nodes 
 
svg.append("g").attr("id", "lnks") 
 
svg.append("g").attr("id", "nds") 
 

 
function new_node(id) { 
 
    this.id = id; 
 
    this.x = w/2; 
 
    this.y = h/2; 
 
} 
 

 
function new_link(source, target) { 
 
    this.source = source; 
 
    this.target = target; 
 
} 
 

 
var nodes = []; 
 
var links = []; 
 

 
var node; 
 

 
var circles; 
 

 
var link; 
 

 
var simulation = d3.forceSimulation() 
 
    .force("link", d3.forceLink().distance(80).id(function(d) { 
 
    return d.id; 
 
    })) 
 
    .force("charge", d3.forceManyBody().strength(-1000)) 
 
    .force("xPos", d3.forceX(w/2)) 
 
    .force("yPos", d3.forceY(h/2)) 
 
    .on('tick', ticked); 
 

 
simulation.stop(); 
 

 
var newNode = new new_node(0); 
 
nodes.push(newNode); 
 

 
for (var i = 1; i < 3; i++) { 
 
    if (i == 3) continue; 
 
    addNode(0, i) 
 
} 
 

 
function addNode(rootId, newId) { 
 

 
    var newNode = new new_node(newId); 
 
    nodes.push(newNode); 
 
    var newLink = new new_link(rootId, newId); 
 
    links.push(newLink); 
 

 
    //adds newest link and draws it 
 
    link = svg.select("#lnks").selectAll(".link") 
 
    .data(links) 
 
    var linkEnter = link 
 
    .enter().append("line") 
 
    .attr("class", "link"); 
 
    link = linkEnter.merge(link); 
 

 
    //adds newest node 
 
    node = svg.select("#nds").selectAll(".node") 
 
    .data(nodes) 
 
    var nodeEnter = node 
 
    .enter().append("g") 
 
    .attr("class", "node"); 
 

 
    //draws circle on newest node 
 
    var circlesEnter = nodeEnter.append('circle') 
 

 
    node = nodeEnter.merge(node); 
 
    circles = d3.selectAll('circle'); 
 

 
    simulation.stop(); 
 

 
    simulation.nodes(nodes); 
 

 
    simulation.force("link") 
 
    .links(links); 
 

 
    restartSim(); 
 
} 
 

 
//starts up the simulation and sets up the way the leaves react to interaction 
 
function restartSim() { 
 
    simulation.restart(); 
 

 
    circles.on('click', function(d, i) { 
 
    addNode(i, nodes.length) 
 
    }) 
 
} 
 

 
function ticked() { 
 
    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; 
 
    }); 
 

 
    node.attr("transform", function(d) { 
 
    return "translate(" + d.x + "," + d.y + ")"; 
 
    }); 
 
}
.link { 
 
    stroke: #bbb; 
 
} 
 
.node circle { 
 
    pointer-events: all; 
 
    fill: black; 
 
    stroke-width: 0px; 
 
    r: 20px 
 
} 
 
h1 { 
 
    color: white; 
 
}
<script src="https://d3js.org/d3.v4.min.js"></script>

代码也放在这里codepen: http://codepen.io/zpenoyre/pen/kkxBRW?editors=0010

力模拟顾名思义是彼此相互作用的粒子模拟。阿尔法被用来通过在每次迭代时衰减来帮助收敛系统。这些力被乘以alpha,所以在每次迭代时力会变得更弱,直到模拟停止时alpha达到非常低的值。从D3文档:

simulation.restart()<>

重新启动模拟的内部定时器,并返回模拟。 结合simulation.alphaTarget或simulation.alpha,此方法可用于在交互期间“模拟”模拟,例如拖动节点时的模拟,或暂时使用simulation.stop暂停模拟时恢复模拟。

当你添加一个节点时,模拟已经停止,所以你需要用alpha1来“模拟”模拟。

simulation.force("link") 
    .links(links); 

    simulation.alpha(1); // <---- reheat; 

    restartSim(); 

这里是更新的代码笔: http://codepen.io/anon/pen/amqrWq?editors=0010

+0

那伟大工程,三江源。有没有一种简单的方法来理解alpha在这里做什么? “再加热”对我来说看起来并不是很直观,[d3 API](https://github.com/d3/d3-force/blob/master/README.md#simulation_alpha)对我来说没有多大意义就此主题而言。 – Zephyr

+0

真棒,我没有意识到模拟有效,但仔细观察,我很清楚地看到它。有意义的是,您希望减少模拟计算,但每次更改设置时都需要重新启动它。谢谢! – Zephyr

+0

@Zephyr从你的第二个评论中,你可能会得到它。但是在'simulation.alphaTarget'提交中,您或其他任何人都可以获得很好的附加信息:(https://github.com/d3/d3-force/commit/8a2c8590bb879c70eb2e10264b41d0200c887be9​​)“这样可以设置”desired“alpha模拟,并使模拟平滑地内插到期望的值,默认情况下,目标值为零,以便模拟冷却,但通过将其设置为 非零值,例如在拖动交互期间,你也可以用它来模拟热量。“ – ibgib