1

This might be about the simplest D3 force layout ever:

const svg = main.append("svg")
    .attr("width",500)
    .attr("height",500)
    .classed("SVG_frame",true)
    .append("g")

const nodes = [{id:1},{id:2}];

const simulation = d3.forceSimulation(nodes)
    .force("centering",d3.forceCenter([200,200]))
    .force("collision",d3.forceCollide(20))

const node = svg
    .selectAll("circle")
    .data(nodes)
    .enter().append("circle")
    .attr("r",20)

simulation.on("tick",function() {
    console.log(nodes[0].x)
    node
        .attr("cx",d => d.x)
        .attr("cy",d => d.y)
})

And yet, I get <circle> attribute cx: Expected length, "NaN". on the first frame. (cy displaces a tiny bit on the frame the simulation gives up moving)

I know this have been asked several times, but none seem to address version 4, where force simulation has presumably changed its inner workings. In fact, the docs even state now that when position is NaN, the position is automatically arranged in a "phyllotaxis arrangement" or whatever, so maybe this isn't supposed to happen, but it does.

Anyone has any clue?

Rafael
  • 557
  • 6
  • 21

1 Answers1

3

The problem here is very simple: d3.forceCenter takes two values, not an array of values:

[d3.forceCenter] Creates a new centering force with the specified x- and y- coordinates. If x and y are not specified, they default to ⟨0,0⟩.

In an API, brackets around an argument mean that the argument is optional (see here). When you see something like this in the D3 API:

d3.forceCenter([x, y])

You have to take care for not mistaking those brackets for an array. Here, [x, y] means that the values are optional, it doesn't mean that they must be in an array.

So, instead of:

.force("centering",d3.forceCenter([200,200]))

It should be:

.force("centering",d3.forceCenter(200,200))

Here is a demo:

const svg = d3.select("body").append("svg")
    .attr("width",500)
    .attr("height",500)
    .classed("SVG_frame",true)
    .append("g")

const nodes = [{id:1},{id:2}];

const simulation = d3.forceSimulation(nodes)
    .force("centering",d3.forceCenter(200,200))
    .force("collision",d3.forceCollide(20))

const node = svg
    .selectAll("circle")
    .data(nodes)
    .enter().append("circle")
    .attr("r",20);

simulation.on("tick",function() {
    node.attr("cx",d => d.x).attr("cy",d => d.y)
});
<script src="https://d3js.org/d3.v4.min.js"></script>
Community
  • 1
  • 1
Gerardo Furtado
  • 100,839
  • 9
  • 121
  • 171
  • 1
    AAAAAAAAAAAAAAAAAAAAA You're right! It worked! I never know when those brackets are arrays or just doc notation. I went for the former this time. :/ I think I'll just assume they're never arrays unless stated otherwise. – Rafael Jan 12 '17 at 15:16