For the past few days, I've been facing a persistent issue that I just can't seem to find a solution for when it comes to updating nodes properly.
Using three.js to render the graph adds an extra layer of complexity, as the information available online is scarce on how to achieve this effectively.
Initially, everything works smoothly upon rendering. However, the problem arises when attempting to add new nodes using the update
function.
The newly added nodes end up disconnected from their parent nodes, and the created links appear to be lacking coordinates and forces.
In the example provided below, I am adding a new node with id: test
.
https://i.sstatic.net/82qt6dAT.png
https://i.sstatic.net/2fbe3TQM.png
Here are the approaches I have experimented with so far:
let root = d3
.stratify()
.id((d) => d.id)
.parentId((d) => d.linkedTo)(data)
let nodes = root.descendants()
let links = root.links()
simulation = d3
.forceSimulation(nodes)
.force('charge', d3.forceManyBody().strength(-1000))
.force('center', d3.forceCenter(0, 0))
.force('collide', d3.forceCollide().radius(50).strength(0.9))
.on('tick', ticked)
simulation.force(
'link',
d3
.forceLink(links)
.id((d) => {
return d.data._id
})
.distance(10)
.strength(0.9)
)
// Render nodes and links on the three scene
links.forEach(renderLink)
nodes.forEach(renderNode)
function update(newData, oldData) {
simulation.stop()
const newRoot = d3
.stratify()
.id((d) => d.id)
.parentId((d) => d.linkedTo)(newData)
const newNodes = newRoot.descendants()
const newLinks = newRoot.links()
// Identify nodes to remove and eliminate them
const nodesToRemove = nodes.filter((node) => !newNodes.some((newNode) => newNode.id === node.id))
// Remove nodes from the three scene
removeNodes(nodesToRemove)
// Identify links to remove and discard them
const linksToRemove = links.filter(
(link) =>
!newLinks.some(
(newLink) => newLink.source.id === link.source.id && newLink.target.id === link.target.id
)
)
// Remove links from the three scene
removeLinks(linksToRemove)
// Discover new nodes
const nodesToAdd = newNodes.filter((newNode) => !nodes.some((node) => node.id === newNode.id))
nodes = [...nodes, ...nodesToAdd]
// Identify links to add and introduce them
const linksToAdd = newLinks.filter(
(newLink) =>
!links.some((link) => {
const sourceMathces = link.source.id === newLink.source.id
const targetMathces = link.target.id === newLink.target.id
return sourceMathces && targetMathces
})
)
links = [...links, ...linksToAdd]
simulation.nodes(nodes).force('link').links(links)
simulation.alpha(0.5).restart()
}
Your assistance is greatly appreciated!