1

我使用 d3-force 进行碰撞检测:

function layout(nodesWithXY) {
  const simulation = forceSimulation(nodesWithXY)
    .force('collide', forceCollide(4.5))
    .stop()
    .tick(300)

  return simulation.nodes()
}

我不希望图表被动画化,所以我只是在 300 个滴答声后得到节点的最终状态。

nodesWithXY但是,如果我可以再次运行模拟(使用相同的)并获得相同的结果,那就太好了。这将使它更容易在反应中使用。

这可能吗?例如,通过运行更多迭代或调整 alpa 和衰减值?

4

2 回答 2

2

所以几周前,我偶然发现了 Mike Bostock 在 2016 年的这篇文章:“使用叶序,d3-force 初始化现在是确定性的!在重新加载和浏览器之间保持一致。” https://twitter.com/mbostock/status/725124754701717504?lang=de

如果我将 x 和 y 的初始值设置为NaN我得到确定性的结果。这在此处有所描述:https ://github.com/d3/d3-force#simulation_nodes

const nodes = d3.range(5).map(() => ({
    // use NaN to make deterministic
    x: NaN,
    y: NaN
}));
const simulation = d3.forceSimulation(nodes)
    .force("collide", d3.forceCollide(10))
    .stop()
    .tick(300)
console.log(nodes.map(d => d.x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

所以只要我不需要初始位置就可以了

于 2020-01-24T11:52:18.087 回答
1

简而言之:D3 力模拟不能也不应该是纯函数(即,如果传入相同的参数,则始终返回相同值的函数)。

原因是模拟,就像一个物理系统,是混乱的,高度依赖于第一次交互是如何发生的。通过它的设计,D3 力模拟是不确定的。

我们可以很容易地在一个基本的模拟中证明这一点:

const nodes = d3.range(5).map(() => ({
  x: 100,
  y: 100
}));
const simulation = d3.forceSimulation(nodes)
  .force("collide", d3.forceCollide(10))
  .stop()
  .tick(300)
console.log(nodes.map(d => d.x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

如果我们多次运行该片段,我们将得到不同的结果。例如,x节点的位置:

第一次:[120.1198、110.0542、100.2805、89.5732、79.972]

第二次:[67.6898, 83.854, 99.5493, 116.7441, 132.1625]

第三次:[133.1773, 116.5792, 100.4626, 82.8064, 66.9742]

ETC...

您可以自己看到,只需单击Run code snippet几次按钮即可。

话虽如此,如果您希望 D3 力模拟表现得像一个纯函数,那么您使用的工具是错误的。

于 2019-09-10T01:37:56.603 回答