javascriptcytoscape.jsdagre

cytoscape.js and dagre result in one node positioned awkwardly


Given the cytoscape.js snippet below, using the dagre layout, can anyone explain why node number 2 positions itself to the bottom right instead of in order like the rest of them?

var cy = cytoscape({
  container: document.getElementById('cy'),
  elements: [{
      data: {
        id: 1477,
        label: "Heading",
      },
    },
    {
      data: {
        id: 1483,
        label: "Number 2",
        parent: 1479,
      },
    },
    {
      data: {
        id: 1479,
        label: "Group",
      },
    },
    {
      data: {
        id: 1478,
        label: "Number 0",
        parent: 1479,
      },
    },
    {
      data: {
        id: 1480,
        source: 1477,
        target: 1478,
        minLen: 1,
      },
    },
    {
      data: {
        id: 1484,
        source: 1481,
        target: 1483,
        minLen: 1,
      },
    },
    {
      data: {
        id: 1481,
        label: "Number 1",
        parent: 1479,
      },
    },
    {
      data: {
        id: 1482,
        source: 1478,
        target: 1481,
        minLen: 1,
      },
    },
    {
      data: {
        id: 1487,
        label: "Number 4",
        parent: 1479,
      },
    },
    {
      data: {
        id: 1485,
        label: "Number 3",
        parent: 1479,
      },
    },
    {
      data: {
        id: 1486,
        source: 1483,
        target: 1485,
        minLen: 1,
      },
    },
    {
      data: {
        id: 1488,
        source: 1485,
        target: 1487,
        minLen: 1,
      },
    },
    {
      data: {
        id: 1490,
        source: 1487,
        target: 1489,
        minLen: 1,
      },
    },
    {
      data: {
        id: 1489,
        label: "Number 5",
        parent: 1479,
      },
    },
    {
      data: {
        id: 1491,
        label: "Final",
      },
    },
    {
      data: {
        id: 1492,
        source: 1489,
        target: 1491,
        minLen: 1,
      },
    },
  ],
  layout: {
    name: 'dagre',
    'nodeSep': 25,
    'rankSep': 10,
  },
  style: [{
      selector: 'node',
      style: {
        label: 'data(label)',
        'text-valign': 'center',
        'text-halign': 'right',
        'text-margin-x': '-155',
        'text-wrap': 'wrap',
        'text-max-width': 150,
        'width': 180,
        'background-fit': 'contain',
        'shape': 'roundrectangle',
        'background-opacity': 0,
        'background-position-x': 0,
        'height': 24,
        'border-width': 1,
        'padding-right': 5,
        'padding-left': 5,
        'padding-top': 5,
        'padding-bottom': 5,
        'text-events': 'yes',
        'font-size': 12,
      }
    },
    {
      selector: 'edge',
      style: {
        'width': 1,
        'curve-style': 'bezier',
        'line-color': 'black',
        'line-style': 'solid',
        'target-arrow-shape': 'triangle-backcurve',
        'target-arrow-color': 'black',
        'text-rotation': 'autorotate',
        'label': 'data(label)',
      }
    },
    {
      selector: '$node > node',
      style: {
        'text-rotation': '-90deg',
        'text-halign': 'left',
        'text-margin-x': -10,
        'text-margin-y': -40,
      }
    },
    {
      selector: '.Badge',
      style: {
        'border-width': 3,
      }
    },
  ],
  minZoom: 0.5,
  maxZoom: 1.5,
  zoomingEnabled: true,
  userZoomingEnabled: false,
  autoungrabify: false,
  autounselectify: true,
});
body { 
  font: 14px helvetica neue, helvetica, arial, sans-serif;
}

#cy {
  height: 100%;
  width: 100%;
  position: absolute;
  left: 0;
  top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.5/cytoscape.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/dagre/0.8.5/dagre.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-dagre@2.2.2/cytoscape-dagre.min.js"></script>


<div id="cy"></div>


Solution

  • With a little re-arragement, you can easily get this to work. Generally, it is better to group the elements by nodes and edges, also in a ascending order. This improves readability and, in this case, prevents inconsistent layouts.

    I think that the issue here stemms from the edges being added to the graph before the corresponding node (node Number 2) is present.

    Here is the working code:

    var cy = cytoscape({
      container: document.getElementById('cy'),
      elements: {
    
        nodes: [{
            data: {
              id: 1477,
              label: "Heading",
            },
          },
          {
            data: {
              id: 1479,
              label: "Group",
            },
          },
          {
            data: {
              id: 1478,
              label: "Number 0",
              parent: 1479,
            },
          },
          {
            data: {
              id: 1481,
              label: "Number 1",
              parent: 1479,
            },
          },
          {
            data: {
              id: 1483,
              label: "Number 2",
              parent: 1479,
            },
          },
          {
            data: {
              id: 1485,
              label: "Number 3",
              parent: 1479,
            },
          },
          {
            data: {
              id: 1487,
              label: "Number 4",
              parent: 1479,
            },
          },
          {
            data: {
              id: 1489,
              label: "Number 5",
              parent: 1479,
            },
          },
          {
            data: {
              id: 1491,
              label: "Final",
            },
          },
        ],
        edges: [{
            data: {
              id: 1480,
              source: 1477,
              target: 1478,
              minLen: 1,
            },
          },
          {
            data: {
              id: 1482,
              source: 1478,
              target: 1481,
              minLen: 1,
            },
          },
          {
            data: {
              id: 1484,
              source: 1481,
              target: 1483,
              minLen: 1,
            },
          },
          {
            data: {
              id: 1486,
              source: 1483,
              target: 1485,
              minLen: 1,
            },
          },
          {
            data: {
              id: 1488,
              source: 1485,
              target: 1487,
              minLen: 1,
            },
          },
          {
            data: {
              id: 1490,
              source: 1487,
              target: 1489,
              minLen: 1,
            },
          },
    
          {
            data: {
              id: 1492,
              source: 1489,
              target: 1491,
              minLen: 1,
            },
          }
        ]
      },
      layout: {
        name: 'dagre',
        'nodeSep': 25,
        'rankSep': 10,
      },
      style: [{
          selector: 'node',
          style: {
            label: 'data(label)',
            'text-valign': 'center',
            'text-halign': 'right',
            'text-margin-x': '-155',
            'text-wrap': 'wrap',
            'text-max-width': 150,
            'width': 180,
            'background-fit': 'contain',
            'shape': 'roundrectangle',
            'background-opacity': 0,
            'background-position-x': 0,
            'height': 24,
            'border-width': 1,
            'padding-right': 5,
            'padding-left': 5,
            'padding-top': 5,
            'padding-bottom': 5,
            'text-events': 'yes',
            'font-size': 12,
          }
        },
        {
          selector: 'edge',
          style: {
            'width': 1,
            'curve-style': 'bezier',
            'line-color': 'black',
            'line-style': 'solid',
            'target-arrow-shape': 'triangle-backcurve',
            'target-arrow-color': 'black',
            'text-rotation': 'autorotate',
            'label': 'data(label)',
          }
        },
        {
          selector: '$node > node',
          style: {
            'text-rotation': '-90deg',
            'text-halign': 'left',
            'text-margin-x': -10,
            'text-margin-y': -40,
          }
        },
        {
          selector: '.Badge',
          style: {
            'border-width': 3,
          }
        },
      ],
      minZoom: 0.5,
      maxZoom: 1.5,
      zoomingEnabled: true,
      userZoomingEnabled: false,
      autoungrabify: false,
      autounselectify: true,
    });
    body {
      font: 14px helvetica neue, helvetica, arial, sans-serif;
    }
    
    #cy {
      height: 100%;
      width: 100%;
      position: absolute;
      left: 0;
      top: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.5/cytoscape.js"></script>
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dagre/0.8.5/dagre.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/cytoscape-dagre@2.2.2/cytoscape-dagre.min.js"></script>
    
    
    <div id="cy"></div>