Why is x, y NaN in this Twin Force Graph?

This twin force graph prototype is originally two different namespaces, which we have condensed and simplified into a single function, but there must be some fundamental logic error in it since this different version both create the same x, y is a NaN error. We seem to have a fundamental logic flaw in our thinking somewhere, can you help spot it please?

Twin Level Try / brettforbes | Observable (observablehq.com)

In our function, we setup up parallel copies of everything, so they can be acted on at some later stage with potential features, rather than having a single function iterated through with data. Since the algorithm normally sits in a namespace, everything is copied along as you can see. In the JS version we have to deal with promises, but in Observable that seems to be handled nicely, thanks.

We run through a four step process:

  1. initSVG(): → setup the svg’s and markers etc.
  2. updateGraph() → split the input graph (node array, edge array) into two independent sub-graphs (Promo and Scratch) based on a condition, using an Adjacency list.
  3. showGraph() → setup the links and the nodes for both charts in parallel,
  4. simGraph() → setup and run both simulations.

The first two steps both execute flawlessly, but somehow there is a flaw in our thinking with the rest. Originally the code was swapped with the initialisation of the simulation objects occurring before showGraph step 3, with only the
.on("tick", "ticked")
function occurring after the nodes and edges were setup.

But that configuration created the NaN errors as well, so it has to be some limitation in our understanding, perhaps about the sequencing of calls. Can you help please?

can you share the notebook? it seems to be private at the moment

1 Like

sorry, it should be public now, let me know if that is still a problem, thanks

I can see the notebook but I can’t find the source of the bug. One issue I saw is that in the zoom event handlers you’re not using the function’s event, but this is not the core issue.

1 Like

Yes its strange, there must be a logic errors somewhere, but i am a bit of a D3 newbie

As a general advice I’d suggest to try and make your code shorter, focus on one thing at a time, and you will see what breaks.

Hmmm, pretty much the only way i could simplify it and maintain the original concept is to comment out half the code, and just try to do one force graph, but using the same process.

However, it does seem that there must be some logic gap, that i hoped would be obvious to the D3 gurus. Some flaw in the way i step through the logic

I would recommend to run the sim only for a couple of ticks, and maybe add assertions to capture when and where you start to get NaNs, e.g.

const num = n => { console.assert(isFinite(n)); return n };
// ...
   .attr('x1', (d) => num(d.source.x))

Once you’ve narrowed the source down a bit you can switch out the assert of a breakpoint that lets you inspect:

const num = n => { if(!isFinite(n)) debugger; return n };

Alternatively you could set conditional breakpoints, e.g.:

1 Like

thats great advice, thank you @mootari

Question how do i run the simulation for only a few ticks? Can you advise that please?

As you say it would be good to narrow down where it goes wrong, and then we can come back with a tighter-defined problem

You immediately call sim.stop() when creating your sim (it’s chainable), and later call sim.tick() to move it forward.

When everything is working you can leave .stop() in place and just call sim.restart() to keep it running again.

1 Like