如何在不输入的情况下添加节点和链接做d3-force

how to add nodes and links do d3-force without enter

我正在尝试更新节点和 links 但不想使用 d3 的输入模式。原因是我希望 svelte 框架代替它执行此操作并处理所有渲染,我只想使用 d3-force 进行计算。

我的初始渲染工作得很好,但是添加 links 和节点有以下问题:

  1. 添加 links 使网络增长
  2. links 似乎对图形布局没有任何影响,即它们似乎没有添加到 force.simulation
  3. 第一次添加节点似乎可行,但是在添加 link 之后添加节点时,它们似乎不会对其他节点产生影响。

这是我添加节点和 links 的函数:

function addNode(){
    force.forceSimulation(data.nodes.push({"id": "116", "group": 5, "index":data.nodes.length, "x":0, "y":0, "vx":0, "vy":0}))
    data=data
    graph.alpha(1.0).update()
    graph.restart()

}

function addLink(){
    let n=getRandomInt(0, data.nodes.length-1)
    let tn=getRandomInt(0, n)
    let sn=getRandomInt(n, data.nodes.length-1)

    graph.force("link", force.forceLink(data.links.push({'source':data.nodes[sn], 'target':data.nodes[tn],'index':data.links.length, 'value':getRandomInt(1, 7)})))

    data=data

    graph.alpha(1.0).update()
    graph.restart()
}

我找到了 答案,但他们使用合并并将其绑定到 DOM 元素。我不明白如何在不涉及 DOM 的情况下执行此操作,只需更新节点数组和 javascript 中的 links 以使 d3-force 将其包含在模拟中。

Here 我在 svelte REPL 中有当前的模拟,你可以 fork 并编辑它。

幸运的是,从 DOM 中分离 d3-force 布局相当容易:力布局本身与 DOM 没有交互,它只是基于某些数据属性的物理计算。虽然添加和删除数据点 (nodes/links) 可能有点棘手,但无论 D3 是否渲染 DOM、其他渲染,或者您根本不渲染力,都是一样的。

这里是你添加链接和节点的地方:

function addNode(){
    force.forceSimulation(data.nodes.push({"id": "116", "group": 5, "index":data.nodes.length, "x":0, "y":0, "vx":0, "vy":0}))
    data=data
    graph.alpha(1.0).update()
    graph.restart()
}

function addLink(){
    let n=getRandomInt(0, data.nodes.length-1)
    let tn=getRandomInt(0, n)
    let sn=getRandomInt(n, data.nodes.length-1)

    graph.force("link", force.forceLink(data.links.push({'source':data.nodes[sn], 'target':data.nodes[tn],'index':data.links.length, 'value':getRandomInt(1, 7)})))

    data=data

    graph.alpha(1.0).update()
    graph.restart()
}

这里有几个问题:

  1. Array.push() 不是 return 数组。它就地修改一个数组,return在推送一个项目后调整数组的长度。这意味着您实际上并没有将节点添加到强制布局。这将导致问题,因为力布局需要对象而不是基元来表示节点。相反,只需推送 node/link,然后将 node/link 数组传递给 .nodes() 或 .links()

  2. force.forceSimulation() 将创建一个新的力布局生成器,这不是您想要的。你想在现有节点上添加节点,所以我们可以使用 graph.nodes() 代替。

  3. 没有force.update(),这会导致错误,这也是您无法在完成冷却后重新启动模拟的原因。我们可以放弃这部分。

让我们看看这两个函数看起来像纠正这个:

function addNode(){
    data.nodes.push({"id": "116", "group": 5, "index":data.nodes.length, "x":0, "y":0, "vx":0, "vy":0})
    graph.nodes(data.nodes)
    data=data
    graph.alpha(1.0).restart()

}

function addLink(){
    let n=getRandomInt(0, data.nodes.length-1)
    let tn=getRandomInt(0, n)
    let sn=getRandomInt(n, data.nodes.length-1)

  data.links.push({'source':data.nodes[sn], 'target':data.nodes[tn],'index':data.links.length, 'value':getRandomInt(1, 7)})
    graph.force("link", force.forceLink(data.links))
    data=data
    graph.alpha(1.0).restart()

}

我不确定你为什么有 data=data,没有它我看不出有什么不同,我会悄悄地假设这是框架的一个怪癖


更新链接的小替代方案:

您可以访问您命名为 'link' 的部队并为其分配新链接:

graph.force("link").links(data.links)

而不是:

graph.force("link", force.forceLink(data.links))

后者重新创建一个力,而第一个只是修改它。