d3.jsd3-force-directed

d3Force's d3.forceLink error: node not found: <node id here>


I see this related solution D3js Force Directed Graph Link not found

yet I get the error in the title - "VM2128 d3-force-3d:2 Uncaught Error: node not found: golfing_4_1"

function setNodeId(node) {
  return node.id; }

Graph.d3Force('link', d3.forceLink(graphData.links)
  .id( (d,i) => setNodeId(d) )
  // .strength( d => linkStrength(d) )
);

apparently(?) using version 2.1.1 of d3-force. Perhaps not even d3 itself

the minimally reproducible code:

const graphData = {
  nodes: [{
      id: "Alice"
    },
    {
      id: "Bob"
    },
    {
      id: "George"
    }
  ],
  links: [{
      source: "Alice",
      target: "George"
    },
    {
      source: "George",
      target: "Bob"
    }
  ]
};

const elem = document.getElementById('3d-graph');
const Graph = ForceGraph3D()(elem)
  .graphData(graphData);

function setNodeId(node) {
  return node.id;
}

function linkStrength(link) {
  return (1 / link.sourceLevel);
}
Graph.d3Force('link', d3.forceLink(graphData.links)
  .id((d, i) => setNodeId(d))
  // .distance(200)
  // .strength( d => linkStrength(d) )
);
<script src="https://unpkg.com/d3-force-3d"></script>
<script src="https://unpkg.com/3d-force-graph"></script>
<div id="3d-graph"></div>


Solution

  • The underlying problem was that you needed to pass the entire graphData to your d3.forceLink function, not just graphData.link. However, I think the following solution is cleaner:

    From the documentation of 3d-force-graph for Graph.d3Force():

    Getter/setter for the internal forces that control the d3 simulation engine. Follows the same interface as d3-force-3d's simulation.force. Three forces are included by default: 'link' (based on forceLink), 'charge' (based on forceManyBody) and 'center' (based on forceCenter).

    This means that you can call the function with only a string 'link' and you get an object of type d3-force-3d.forceLink, which is automatically applied and thus automatically has access to the right graphData. Then, you can set forceLink.id just like you did, but you can also set it later.

    const graphData = {
      nodes: [{
          id: "Alice"
        },
        {
          id: "Bob"
        },
        {
          id: "George"
        }
      ],
      links: [{
          source: "Alice",
          target: "George"
        },
        {
          source: "George",
          target: "Bob"
        }
      ]
    };
    
    const elem = document.getElementById('3d-graph');
    const Graph = ForceGraph3D()(elem)
      .graphData(graphData);
    
    function setNodeId(node) {
      return node.id;
    }
    
    const linkForce = Graph.d3Force('link')
      .id((d, i) => setNodeId(d));
    <script src="https://unpkg.com/d3-force-3d@2.2.0/dist/d3-force-3d.js"></script>
    <script src="https://unpkg.com/3d-force-graph@1.67.3/dist/3d-force-graph.js"></script>
    <div id="3d-graph"></div>