Arranging multiple trees with ArrangingLayout

Hello! I have several trees that I want to show in a column. I want them to be centered on the root node of the tree (see screenshot below - I want to lineup the root nodes indicated in red).

I’m using the ArrangingLayout with a GridLayout to arrange the trees in a column. Then on each tree I’m using a Double Tree Layout.

What I’ve tried:

In order to center each tree on the root node, in the ArrangingLayout I tried to set the location of the tree to be the location of the root node, but when I print out location it was the same as before I tried to change it to match the root node.

Here’s the _addMainNode function of the ArrangingLayout with the code I added to set the location:

_addMainNode(groups, subcoll, diagram) {
        const grp = new go.Node();

        // My code:
        // find root node and set location of the group to be the location of the root node so that arrangingLayout can center the subgraphs on the roots
        const root = subcoll.values().find((part) => {
          return (part instanceof go.Node)
        }).findTreeRoot();
        console.log("root default location", root.location)
        root.locationSpot = go.Spot.Center;
        grp.location = root.location;
        console.log("root center location", root.location)
        console.log("group location", grp.location)
        // end of code I added

        const grpb = diagram.computePartsBounds(subcoll);
        grp.desiredSize = grpb.size;
        grp.position = grpb.position;
        groups.add(grp, { parts: subcoll, bounds: grpb });
    }

Reading the documentation on location, it sounds like it is controlled by locationSpot. But I want it to set it to a specific location (the root node), not a relative locationSpot like center or top left.

Any suggestions? Thanks.

I’d probably replace the GridLayout that you have arranging the double trees vertically with a custom layout that knows about those special nodes that you have in each double tree that you want to line up in a vertical line. Let me see if I can find a sample for you that is vaguely similar to what you want.

If I understand what you want correctly, I think this demonstrates one solution. You may need to adapt the code for your data and other conventions and requirements that your app has.

<!DOCTYPE html>
<html>
<head>
  <title>Aligning DoubleTree Root Nodes</title>
  <!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
  <textarea id="mySavedModel" style="width:100%;height:300px"></textarea>

  <script src="https://unpkg.com/gojs"></script>
  <script src="https://unpkg.com/create-gojs-kit/dist/extensions/ArrangingLayout.js"></script>
  <script src="https://unpkg.com/create-gojs-kit/dist/extensions/DoubleTreeLayout.js"></script>
  <script id="code">
class CustomArrangingLayout extends ArrangingLayout {
  moveSubgraph(subColl/*: go.Set<go.Part>*/, subbounds, bounds) {
    const diagram = this.diagram;
    if (!diagram) return;
    let root = null;
    subColl.each(n => {
      if (n.isTreeRoot) root = n;
    });
    diagram.moveParts(subColl, new go.Point(bounds.centerX - root.actualBounds.centerX, bounds.y - subbounds.y));
  }
}

const myDiagram =
  new go.Diagram("myDiagramDiv", {
      layout: new CustomArrangingLayout({
          arrangingLayout: new go.GridLayout({ wrappingColumn: 1 }),
          primaryLayout: new DoubleTreeLayout({
            directionFunction: n => n.data.dir !== "left"
          })
        }),
      "undoManager.isEnabled": true,
      "ModelChanged": e => {     // just for demonstration purposes,
        if (e.isTransactionFinished) {  // show the model data in the page's TextArea
          document.getElementById("mySavedModel").textContent = e.model.toJson();
        }
      }
    });

myDiagram.nodeTemplate =
  new go.Node("Auto")
    .add(
      new go.Shape({ fill: "white" })
        .bind("fill", "color"),
      new go.TextBlock({ margin: 8 })
        .bind("text")
    );

myDiagram.model = new go.GraphLinksModel(
[
  { key: 1, text: "Alpha", color: "lightblue" },
  { key: 2, text: "Beta", color: "orange" },
  { key: 3, text: "Gamma", color: "lightgreen" },
  { key: 4, text: "Delta", color: "pink" },
  { key: 11, text: "Alpha", color: "lightblue" },
  { key: 12, text: "Beta", color: "orange" },
  { key: 13, text: "Gamma", color: "lightgreen", dir: "left" },
  { key: 14, text: "Delta", color: "pink" },
  { key: 21, text: "Alpha", color: "lightblue" },
  { key: 22, text: "Beta", color: "orange", dir: "left" },
  { key: 23, text: "Gamma", color: "lightgreen" },
  { key: 24, text: "Delta", color: "pink" },
],
[
  { from: 1, to: 2 },
  { from: 1, to: 3 },
  { from: 3, to: 4 },
  { from: 11, to: 12 },
  { from: 11, to: 13 },
  { from: 13, to: 14 },
  { from: 21, to: 22 },
  { from: 22, to: 23 },
  { from: 23, to: 24 },
]);
  </script>
</body>
</html>

That custom layout is exactly what I was trying to do. Thanks Walter!