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();
那伟大工程,三江源。有没有一种简单的方法来理解alpha在这里做什么? “再加热”对我来说看起来并不是很直观,[d3 API](https://github.com/d3/d3-force/blob/master/README.md#simulation_alpha)对我来说没有多大意义就此主题而言。 – Zephyr
真棒,我没有意识到模拟有效,但仔细观察,我很清楚地看到它。有意义的是,您希望减少模拟计算,但每次更改设置时都需要重新启动它。谢谢! – Zephyr
@Zephyr从你的第二个评论中,你可能会得到它。但是在'simulation.alphaTarget'提交中,您或其他任何人都可以获得很好的附加信息:(https://github.com/d3/d3-force/commit/8a2c8590bb879c70eb2e10264b41d0200c887be9)“这样可以设置”desired“alpha模拟,并使模拟平滑地内插到期望的值,默认情况下,目标值为零,以便模拟冷却,但通过将其设置为 非零值,例如在拖动交互期间,你也可以用它来模拟热量。“ – ibgib