Performance location data binding

Hi,

I’m running the following example:

<!DOCTYPE html>
<html>

<head>
  <title>Performance number of nodes</title>
  <meta charset="UTF-8">
</head>

<body onload="init()">
  <span id="myNumberOfNodes"></span>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:1000px"></div>
  <div id="myOverviewDiv" style="border: solid 1px black; width:300px; height:300px"></div>

  <script src="go.js"></script>
  <script id="code">
    function init() {
      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myDiagram =
        $(go.Diagram, "myDiagramDiv",  // create a Diagram for the DIV HTML element
          { // enable undo & redo
            "undoManager.isEnabled": true
          });

      // define a simple Node template
      myDiagram.nodeTemplate =
        $(go.Node, "Auto",  // the Shape will go around the TextBlock
          // new go.Binding("location", "location", go.Point.parse),
          // new go.Binding("location", "location", go.Point.parse).makeTwoWay(go.Point.stringify),
          $(go.Shape, "RoundedRectangle",
            { strokeWidth: 0, fill: "white" },  // default fill is white
            // Shape.fill is bound to Node.data.color
            new go.Binding("fill", "color")),
          $(go.TextBlock,
            { margin: 8 },  // some room around the text
            // TextBlock.text is bound to Node.data.key
            new go.Binding("text", "key"))
        );

        var myOverview =
          $(go.Overview, "myOverviewDiv", { observed: myDiagram, updateDelay: 5000 });
      // but use the default Link template, by not setting Diagram.linkTemplate

      // create the model data that will be represented by Nodes and Links
      let nodes = []
      let links = []
      let numberOfNodes = 50000
      for (let i = 0; i < numberOfNodes; ++i) {
        nodes.push({ key: `Alpha${i}`, color: "lightblue", location: "0 0" })
        links.push({ from: `Alpha${i}`, to: `Alpha${i+1}` })
      }

      myDiagram.model = new go.GraphLinksModel(nodes, links);

      document.getElementById("myNumberOfNodes").innerText = `Number of nodes: ${numberOfNodes}`
    }
  </script>
</body>

</html>

The diagram takes some time to initialize but when it is loaded, I can manipulate the nodes smoothly without any “lagging experience”.

However, when I introduce one or two way databinding on the location, manipulating a nodes position feels very laggy.

I can imagine that two way data binding would influence the location manipulation performance but why is one way data binding influencing this that much? It this expected behavior or am I missing something? Furthermore, I would like to update my model when the user has finished dragging a node; would it help to listen to SelectionMoved events and update the model manually instead of having a two way binding on the location? If I recall correctly, it is not possible to configure the two way data binding that it will only be evaluated on user interaction end.

Thanks!

The problem with your app when you add a Binding on the Node.location property is that you have set the locations of all of the nodes to be at (0, 0).

That means all 50000 nodes and all 50000 links are in the viewport at once, overlapping each other. The severe “lagging” that you see is due to how long it takes to draw all of those nodes and all of those links.

If you do not set the data.location property on your node data, the Diagram.layout would arrange all of the nodes so that they are spread out. Only a few nodes and links would be visible in the viewport, so the drawing time is short – hence no “lag”.

If you really want to have all of those nodes and links be on top of each other, sorry, but it’s going to be slow.

Didn’t think about this; this make sense. Could you elaborate on the last question:

Furthermore, I would like to update my model when the user has finished dragging a node; would it help to listen to SelectionMoved events and update the model manually instead of having a two way binding on the location? If I recall correctly, it is not possible to configure the two way data binding that it will only be evaluated on user interaction end.

That’s correct – bindings are always evaluated immediately.

Yes, you could implement a Model Changed event listener to update model data at the end of each transaction. GoJS Changed Events -- Northwoods Software

Thanks Walter!