user4534236
user4534236

Reputation:

d3 force layout nodes in predictable order

Whenever you refresh this example, the nodes are in a different order. How could you make it so the order is the same upon each refresh? Perhaps "sub node 1" at the top, then adjacent to the right is "sub node 2", etc, all the way around. Other than that, this example works for what I need to achieve.

The solutions below seem to require fixing the nodes to x,y points. But doing that seems to eliminate the drag functionality (the nodes need to be able to be dragged into different locations to change the order). Also, I don't always know how many nodes there will be initially.

https://groups.google.com/forum/#!topic/d3-js/NsHlubbv3pc

Calm down initial tick of a force layout

Configure fixed-layout static graph in d3.js

While the drag is a requirement, the animation is not. I tried seeing if stoping the animation had any effect, but it didn't.

var n = 50;

for (var i = 0; i < n; ++i) force.tick();

force.stop();

Also, tried adding a new property to the data giving each child a rank to manipulate somehow. And tried assigning id of the rank and sorting. Also tried using the index number of the array of objects. No luck. Thanks for any ideas.

Upvotes: 2

Views: 1152

Answers (2)

user4534236
user4534236

Reputation:

Per Lars' comment, it seems this is not possible in a force layout. Switched to tree layout. JSFIDDLE Still need to add the drag piece. Will update if I can get that working.

Here is the JS code:

var diameter = 800;

var tree = d3.layout.tree()
    .size([360, diameter / 2 - 120])
    .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });

var svg = d3.select("body").append("svg")
    .attr("width", diameter)
    .attr("height", diameter)
    .append("g")
    .attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
      
var data = {
 "name": "root",
 "children": [
    {"name": "circle 1"},
    {"name": "circle 2"},
    {"name": "circle 3"},
    {"name": "circle 4"},
    {"name": "circle 5"},
    {"name": "circle 6"},
    {"name": "circle 7"},
    {"name": "circle 8"},
    {"name": "circle 9"},
    {"name": "circle 10"},
    {"name": "circle 11"},
    {"name": "circle 12"},
    {"name": "circle 13"}
 ]
}

    var nodes = tree.nodes(data),
        links = tree.links(nodes);

    var lines = svg.selectAll('line')
        .data(links)
        .enter()
        .append('line')
        .attr('stroke','#ccc');

    lines.attr('x1',function(d){ return d.source.y })
        .attr('y1',function(d){ return d.source.x/ 180 * Math.PI })
        .attr('x2',function(d){ return d.target.y })
        .attr('y2',function(d){ return d.target.x / 180 * Math.PI });

    lines.attr("transform", function(d) {      
        return "rotate(" + (d.target.x - 90 ) + ")"; });

    var link = svg.selectAll(".link")
        .data(links)
        .enter().append("path")
        .attr("class", "link");

    var node = svg.selectAll(".node")
        .data(nodes)
        .enter().append("g")
        .attr("class", "node")
        .attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")rotate(" + (-d.x + 90) + ")"; });

    node.append("circle")
        .attr("r", 48)
        .style("fill", "#ccc")
        .on("dblclick", function(d){
            d3.select(this)
                .attr('r', function(d) {
                    if (d.group === "Hub") {
                        return 80;
                    } else {
                        return 30;
                    }
                })
            }
        );

    node.append("text")
        .attr("dy", ".31em")
        .attr("text-anchor", "middle")
        .text(function(d) { return d.name; });

d3.select(self.frameElement).style("height", diameter - 150 + "px");

Upvotes: 0

Jack Allan
Jack Allan

Reputation: 15004

The reason it is not the same each time is d3 uses a random number to prevent nodes getting stuck in odd places. In my limited understanding the random number acts to "jiggle" the graph to help it untangle itself.

A simple solution to making it predictable is to replace the random number generator with a predictable random number generator such as that provided by seedrandom.

If you seed the random number generator with the same seed before running the simulation the results will be the same every time.

Note only a small change to the graph will cause the graph to have a completely different layout. (I always think of the butterfly chaos effect to explain why).

Upvotes: 0

Related Questions