LayeredDigraphLayout Arrangements of Disconnected Nodes

We are having a graph which uses LayeredDigraphLayout which has some linked and non-linked nodes. The graph is vertically aligned and looks like a tree. Here is a screenshot of the same -

In the above sample A & B is not linked to any node. We want such nodes to arrange downside the tree instead on right side of the leaf node. Is it possible to achieve this without extending/overriding the layout?

No, LayeredDigraphLayout doesn’t have a trivial way to arrange disconnected subsets in a different manner. But if you wanted them to be at the top instead of at the bottom, you could set LayeredDigraphLayout.layeringOption to go.LayeredDigraphLayout.LayerLongestPathSource.

If you really want to put those nodes separately below the regular graph, there are several ways to do so. My guess is that it would be most natural to override LayeredDigraphLayout.makeNetwork to remove all of the LayoutVertexes that have no connected LayoutEdges, and to remember them so that in an override of LayeredDigraphLayout.commitLayout you can position those singleton nodes where you want them.

Thanks for the tips. I was able to arrange the disconnected nodes as intended by using the technique that you mentioned (Note: I used doLayout, instead of makeNetwork as it was throwing an error which I was not able to figure out).

One more question which is somewhat related to this thread.

In the above diagram when X has more siblings, it goes & sits between Y and Z. Could you please point out what might have caused this? The diagram looks even messy when we have more data, I am just not able to find a pattern on what basis the the nodes are arranged.

As a side note, do you have a skype/personalized support system where I can discuss our problems?

Here’s what I just tried. This includes overrides of makeNetwork and commitLayout. I cannot reproduce the odd behavior that you reported.

  function ArrangingLDLayout() {
    go.LayeredDigraphLayout.call(this);
    this._singletons = null;
  }
  go.Diagram.inherit(ArrangingLDLayout, go.LayeredDigraphLayout);

  ArrangingLDLayout.prototype.makeNetwork = function(coll) {
    var net = go.LayeredDigraphLayout.prototype.makeNetwork.call(this, coll);
    // delete all vertexes that have no edges
    var singletons = new go.Set();
    net.vertexes.each(function(v) {
      if (v.edgesCount === 0 && v.node !== null) singletons.add(v.node);
    });
    singletons.each(function(n) {
      net.deleteNode(n);
    });
    this._singletons = singletons;  // remember for commitLayout
    return net;
  };

  ArrangingLDLayout.prototype.commitLayout = function() {
    go.LayeredDigraphLayout.prototype.commitLayout.call(this);
    var p = this.arrangementOrigin.copy();
    p.x += this.columnSpacing/2;  // horizontal indent
    this.network.vertexes.each(function(v) {
      var n = v.node;
      if (n === null) return;
      p.y = Math.max(p.y, n.actualBounds.bottom);
    });
    p.y += 50; // vertical spacing
    var hspace = this.columnSpacing;
    this._singletons.each(function(n) {
      n.move(p);
      p.x += n.actualBounds.width + 50;  // horizontal spacing
    });
  };


  function init() {
    var $ = go.GraphObject.make;

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          initialContentAlignment: go.Spot.Center,
          layout: $(ArrangingLDLayout, //go.LayeredDigraphLayout,
            { direction: 90, setsPortSpots: false })
        });

    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        { fromSpot: go.Spot.TopBottomSides, toSpot: go.Spot.TopBottomSides },
        $(go.Shape, { fill: "whitesmoke", stroke: "lightgray" }),
        $(go.TextBlock, { margin: new go.Margin(10, 30) },
          new go.Binding("text", "key"))
      );

    myDiagram.model = new go.GraphLinksModel([
      { key: "R" },
      { key: "S" },
      { key: "T" },
      { key: "U" },
      { key: "X" },
      { key: "X2" },
      { key: "X3" },
      { key: "Y" },
      { key: "Z" },
      { key: "A" },
      { key: "B" },
    ], [
      { from: "R", to: "T" },
      { from: "R", to: "U" },
      { from: "S", to: "X" },
      //{ from: "S", to: "X2" },
      //{ from: "S", to: "X3" },
      { from: "U", to: "Y" },
      { from: "U", to: "Z" }
    ]);
  }