Search code examples
javascriptvis.jsvis.js-network

Vis.js node layout not behaving as expected when nodes have coordinates


I'm having some trouble understanding the behavior of nodes in a vis.js network when the nodes have coordinates specified. In the code below I have made 3 examples where example 1 and example 3 is behaving as expected, but example 2 is behaving unexpectedly.

In example 2, I have specified coordinates to the nodes and that layout is not fixed. The network have physics turned on initially and then when stabilized physics should be turned off.

I expected that the nodes would be placed just like in example 1, but the nodes should be draggable and when dragging then the nodes should behave like in example 3. But it looks like physics is still turned on and the nodes are not placed as in example 1.

I cannot see what I'm doing wrong. Can anyone help?

var options = {
  physics: { enabled: true, solver: 'repulsion' },
  edges: { smooth: false },
  nodes: { shape: 'box' },
  layout: { hierarchical: { enabled: false } }
};
var arrowAttr = {  to: { enabled: true, type: "triangle" }};
var edges = new vis.DataSet([
  {arrows: arrowAttr, from: "1", to: "2", hidden: false},
  {arrows: arrowAttr, from: "3", to: "4", hidden: false},
  {arrows: arrowAttr, from: "1", to: "4", hidden: false},
  {arrows: arrowAttr, from: "2", to: "3", hidden: false},
]);

// Example 1
var nodes1 = new vis.DataSet([
  {id: "1", label: "Ex 1, Node 1", x: -50, y: -50, fixed: { x: true, y: true}},
  {id: "2", label: "Ex 1, Node 2", x: -50, y: 50, fixed: { x: true, y: true}},
  {id: "3", label: "Ex 1, Node 3", x: 50, y: -50, fixed: { x: true, y: true}},
  {id: "4", label: "Ex 1, Node 4", x: 50, y: 50, fixed: { x: true, y: true}}
]);
var data1 = {
  nodes: nodes1,
  edges: edges
};  
var container1 = document.getElementById('network1');
var network = new vis.Network(container1, data1, options);
network.on("stabilizationIterationsDone", function () {
  network.setOptions( { physics: false } );
});

// Example 2
var nodes2 = new vis.DataSet([
  {id: "1", label: "Ex 2, Node 1", x: -50, y: -50, fixed: { x: false, y: false}},
  {id: "2", label: "Ex 2, Node 2", x: -50, y: 50, fixed: { x: false, y: false}},
  {id: "3", label: "Ex 2, Node 3", x: 50, y: -50, fixed: { x: false, y: false}},
  {id: "4", label: "Ex 2, Node 4", x: 50, y: 50, fixed: { x: false, y: false}}
]);
var data2 = {
  nodes: nodes2,
  edges: edges
};
options.layout.randomSeed = Math.random();
var container2 = document.getElementById('network2');
var network = new vis.Network(container2, data2, options);
network.on("stabilizationIterationsDone", function () {
  network.setOptions( { physics: false } );
});

// Example 3
var nodes3 = new vis.DataSet([
  {id: "1", label: "Ex 3, Node 1"},
  {id: "2", label: "Ex 3, Node 2"},
  {id: "3", label: "Ex 3, Node 3"},
  {id: "4", label: "Ex 3, Node 4"}
]);
var data3 = {
  nodes: nodes3,
  edges: edges
};
options.layout.randomSeed = Math.random();
var container3 = document.getElementById('network3');
var network = new vis.Network(container3, data3, options);
network.on("stabilizationIterationsDone", function () {
  network.setOptions( { physics: false } );
});
<script type="text/javascript" src="https://unpkg.com/[email protected]/dist/vis-network.min.js"></script>
Example 1: Behaves as expected. Nodes are fixed to the coordinates specified.
<div id="network1" style="width: 250px; height: 250px; border: 1px solid #000"></div>
Example 2: Does not behave as expected. Why is the random layout ignored? And physics is not turned off as expected when dragging nodes. Physics is correctly turned off in the next example 3 with the same code.
<div id="network2" style="width: 250px; height: 250px; border: 1px solid #000"></div>
Example 3: Behaves as expected. Nodes are assigned new coordinates randomly on every reload and physics is turned off correctly as specified in the code.
<div id="network3" style="width: 250px; height: 250px; border: 1px solid #000"></div>


Solution

  • If the goal is to initialize the network per Example 1, and have the ability to move nodes per Example 3 - then one proposal is to simply use your Example 1 code example but in the stabilizationIterationsDone event, update each node in nodes1 to make fixed: false. This is shown below:

    var options = {
      physics: { enabled: true, solver: 'repulsion' },
      edges: { smooth: false },
      nodes: { shape: 'box' },
      layout: { hierarchical: { enabled: false } }
    };
    var arrowAttr = {  to: { enabled: true, type: "triangle" }};
    var edges = new vis.DataSet([
      {arrows: arrowAttr, from: "1", to: "2", hidden: false},
      {arrows: arrowAttr, from: "3", to: "4", hidden: false},
      {arrows: arrowAttr, from: "1", to: "4", hidden: false},
      {arrows: arrowAttr, from: "2", to: "3", hidden: false},
    ]);
    
    // Example 1
    var nodes1 = new vis.DataSet([
      {id: "1", label: "Ex 1, Node 1", x: -50, y: -50, fixed: { x: true, y: true}},
      {id: "2", label: "Ex 1, Node 2", x: -50, y: 50, fixed: { x: true, y: true}},
      {id: "3", label: "Ex 1, Node 3", x: 50, y: -50, fixed: { x: true, y: true}},
      {id: "4", label: "Ex 1, Node 4", x: 50, y: 50, fixed: { x: true, y: true}}
    ]);
    var data1 = {
      nodes: nodes1,
      edges: edges
    };  
    var container1 = document.getElementById('network1');
    var network1 = new vis.Network(container1, data1, options);
    network1.on("stabilizationIterationsDone", function () {
      network1.setOptions( { physics: false } );
      // now update data set so that each node is no longer fixed
      nodes1.forEach(function(node) {
        nodes1.update({id: node.id, fixed: false});
      });
    });
    <script type="text/javascript" src="https://unpkg.com/[email protected]/dist/vis-network.min.js"></script>
    <div id="network1" style="width: 250px; height: 250px; border: 1px solid #000"></div>

    Regarding the unexpected nature of the phsyics in Example 2 - you use var network = ... for each example. If you replace this with var network1 = ..., var network2 = ... etc then this problem disappears:

    var options = {
      physics: { enabled: true, solver: 'repulsion' },
      edges: { smooth: false },
      nodes: { shape: 'box' },
      layout: { hierarchical: { enabled: false } }
    };
    var arrowAttr = {  to: { enabled: true, type: "triangle" }};
    var edges = new vis.DataSet([
      {arrows: arrowAttr, from: "1", to: "2", hidden: false},
      {arrows: arrowAttr, from: "3", to: "4", hidden: false},
      {arrows: arrowAttr, from: "1", to: "4", hidden: false},
      {arrows: arrowAttr, from: "2", to: "3", hidden: false},
    ]);
    
    // Example 1
    var nodes1 = new vis.DataSet([
      {id: "1", label: "Ex 1, Node 1", x: -50, y: -50, fixed: { x: true, y: true}},
      {id: "2", label: "Ex 1, Node 2", x: -50, y: 50, fixed: { x: true, y: true}},
      {id: "3", label: "Ex 1, Node 3", x: 50, y: -50, fixed: { x: true, y: true}},
      {id: "4", label: "Ex 1, Node 4", x: 50, y: 50, fixed: { x: true, y: true}}
    ]);
    var data1 = {
      nodes: nodes1,
      edges: edges
    };  
    var container1 = document.getElementById('network1');
    var network1 = new vis.Network(container1, data1, options);
    network1.on("stabilizationIterationsDone", function () {
      network1.setOptions( { physics: false } );
    });
    
    // Example 2
    var nodes2 = new vis.DataSet([
      {id: "1", label: "Ex 2, Node 1", x: -50, y: -50, fixed: { x: false, y: false}},
      {id: "2", label: "Ex 2, Node 2", x: -50, y: 50, fixed: { x: false, y: false}},
      {id: "3", label: "Ex 2, Node 3", x: 50, y: -50, fixed: { x: false, y: false}},
      {id: "4", label: "Ex 2, Node 4", x: 50, y: 50, fixed: { x: false, y: false}}
    ]);
    var data2 = {
      nodes: nodes2,
      edges: edges
    };
    options.layout.randomSeed = Math.random();
    var container2 = document.getElementById('network2');
    var network2 = new vis.Network(container2, data2, options);
    network2.on("stabilizationIterationsDone", function () {
      network2.setOptions( { physics: false } );
    });
    
    // Example 3
    var nodes3 = new vis.DataSet([
      {id: "1", label: "Ex 3, Node 1"},
      {id: "2", label: "Ex 3, Node 2"},
      {id: "3", label: "Ex 3, Node 3"},
      {id: "4", label: "Ex 3, Node 4"}
    ]);
    var data3 = {
      nodes: nodes3,
      edges: edges
    };
    options.layout.randomSeed = Math.random();
    var container3 = document.getElementById('network3');
    var network3 = new vis.Network(container3, data3, options);
    network3.on("stabilizationIterationsDone", function () {
      network3.setOptions( { physics: false } );
    });
    <script type="text/javascript" src="https://unpkg.com/[email protected]/dist/vis-network.min.js"></script>
    Example 1: Behaves as expected. Nodes are fixed to the coordinates specified.
    <div id="network1" style="width: 250px; height: 250px; border: 1px solid #000"></div>
    Example 2: Does not behave as expected. Why is the random layout ignored? And physics is not turned off as expected when dragging nodes. Physics is correctly turned off in the next example 3 with the same code.
    <div id="network2" style="width: 250px; height: 250px; border: 1px solid #000"></div>
    Example 3: Behaves as expected. Nodes are assigned new coordinates randomly on every reload and physics is turned off correctly as specified in the code.
    <div id="network3" style="width: 250px; height: 250px; border: 1px solid #000"></div>

    The elongated layout is still present so I would refer to the initial proposal to remedy that.