Align all nodes on canvas to left

Hi,

How can I align all the nodes on the canvas to particular direction (left or right), the digaram is using LayeredDigraphLayout layout, below images explains it much better.

Default Result:



Required Result:

Thanks

Try using this custom layout instead:

[code] function AlignedLayeredDigraphLayout() {
go.LayeredDigraphLayout.call(this);
}
go.Diagram.inherit(AlignedLayeredDigraphLayout, go.LayeredDigraphLayout);

AlignedLayeredDigraphLayout.prototype.assignLayers = function() {
go.LayeredDigraphLayout.prototype.assignLayers.call(this);
var horiz = this.direction == 0.0 || this.direction == 180.0;
// for every vertex, record the maximum vertex width or height for the vertex’s layer
var maxsizes = [];
for (var it = this.network.vertexes.iterator; it.next(); ) {
var v = it.value;
var lay = v.layer;
var max = maxsizes[lay];
if (max === undefined) max = 0;
var sz = (horiz ? v.width : v.height);
if (sz > max) maxsizes[lay] = sz;
}
// now make sure every vertex has the maximum width or height according to which layer it is in,
// and aligned on the left (if horizontal) or the top (if vertical)
for (it = this.network.vertexes.iterator; it.next(); ) {
var v = it.value;
var node = v.node;
if (node === null) continue;
var lay = v.layer;
var max = maxsizes[lay];
if (horiz) {
v.focus = new go.Point(max/2, v.focus.y);
v.width = max;
} else {
v.focus = new go.Point(v.focus.x, max/2);
v.height = max;
}
}
// from now on, the LayeredDigraphLayout will think that the Node is bigger than it really is
// (other than the ones that are the widest or tallest in their respective layer).
};

AlignedLayeredDigraphLayout.prototype.createNetwork = function() {
return new AlignedLayeredDigraphNetwork();
};

function AlignedLayeredDigraphNetwork() {
go.LayeredDigraphNetwork.call(this);
}
go.Diagram.inherit(AlignedLayeredDigraphNetwork, go.LayeredDigraphNetwork);

AlignedLayeredDigraphNetwork.prototype.createVertex = function() {
return new AlignedLayeredDigraphVertex();
};

function AlignedLayeredDigraphVertex() {
go.LayeredDigraphVertex.call(this);
}
go.Diagram.inherit(AlignedLayeredDigraphVertex, go.LayeredDigraphVertex);

AlignedLayeredDigraphVertex.prototype.commit = function() {
var node = this.node;
if (node !== null) {
node.move(this.bounds.position);
}
};
// end AlignedLayeredDigraph[Layout/Network/Vertex][/code]
We haven’t gotten around to making this an option of the LayeredDigraphLayout class itself.

Hello,
The above code is not working anymore in the 2.2.5 version.
I would be very happy if you rewrite it, please.

Regards,
Michael.

Here’s the samples/ldLayout.html sample but using AlignedLayeredDigraphLayout instead.

<!DOCTYPE html>
<html>
<head>
  <title>Layered Digraph Layout</title>
  <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">

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

    myDiagram =
      $(go.Diagram, "myDiagramDiv",  // must be the ID or reference to div
        {
          initialAutoScale: go.Diagram.UniformToFill,
          layout: $(AlignedLayeredDigraphLayout)
          // other Layout properties are set by the layout function, defined below
        });

    // define the Node template
    myDiagram.nodeTemplate =
      $(go.Node, "Spot",
        $(go.Shape, "Rectangle",
          { fill: "lightgray",  // the initial value, but data-binding may provide different value
            stroke: "black",
            desiredSize: new go.Size(30, 30) },
          new go.Binding("fill", "fill"),
          new go.Binding("desiredSize", "size")),
        $(go.TextBlock,
          { alignment: go.Spot.Bottom, alignmentFocus: go.Spot.Top},
          new go.Binding("text", "text")));

    // define the Link template to be minimal
    myDiagram.linkTemplate =
      $(go.Link,
        { selectable: false },
        $(go.Shape));

    // generate a tree with the default values
    rebuildGraph();
  }

  function AlignedLayeredDigraphLayout() {
    go.LayeredDigraphLayout.call(this);
  }
  go.Diagram.inherit(AlignedLayeredDigraphLayout, go.LayeredDigraphLayout);

  AlignedLayeredDigraphLayout.prototype.assignLayers = function() {
    go.LayeredDigraphLayout.prototype.assignLayers.call(this);
    var horiz = this.direction == 0 || this.direction == 180;
    // for every vertex, record the maximum vertex width or height for the vertex's layer
    var maxsizes = [];
    for (var it = this.network.vertexes.iterator; it.next(); ) {
      var v = it.value;
      var lay = v.layer;
      var max = maxsizes[lay];
      if (max === undefined) max = 0;
      var sz = (horiz ? v.width : v.height);
      if (sz > max) maxsizes[lay] = sz;
    }
    // now make sure every vertex has the maximum width or height according to which layer it is in,
    // and aligned on the left (if horizontal) or the top (if vertical)
    for (it = this.network.vertexes.iterator; it.next(); ) {
      var v = it.value;
      var node = v.node;
      if (node === null) continue;
      var lay = v.layer;
      var max = maxsizes[lay];
      if (horiz) {
        v.focus = new go.Point(max/2, v.focus.y);
        v.width = max;
      } else {
        v.focus = new go.Point(v.focus.x, max/2);
        v.height = max;
      }
    }
    // from now on, the LayeredDigraphLayout will think that the Node is bigger than it really is
    // (other than the ones that are the widest or tallest in their respective layer).
  };

  AlignedLayeredDigraphLayout.prototype.createNetwork = function() {
    return new AlignedLayeredDigraphNetwork(this);
  };


  function AlignedLayeredDigraphNetwork(lay) {
    go.LayeredDigraphNetwork.call(this, lay);
  }
  go.Diagram.inherit(AlignedLayeredDigraphNetwork, go.LayeredDigraphNetwork);

  AlignedLayeredDigraphNetwork.prototype.createVertex = function() {
    return new AlignedLayeredDigraphVertex(this);
  };


  function AlignedLayeredDigraphVertex(net) {
    go.LayeredDigraphVertex.call(this, net);
  }
  go.Diagram.inherit(AlignedLayeredDigraphVertex, go.LayeredDigraphVertex);

  AlignedLayeredDigraphVertex.prototype.commit = function() {
    var node = this.node;
    if (node !== null) {
      node.move(this.bounds.position);
    }
  };
  // end AlignedLayeredDigraph[Layout/Network/Vertex]

  function rebuildGraph() {
    var minNodes = document.getElementById("minNodes").value;
    minNodes = parseInt(minNodes, 10);

    var maxNodes = document.getElementById("maxNodes").value;
    maxNodes = parseInt(maxNodes, 10);

    generateDigraph(minNodes, maxNodes);
  }

  function generateDigraph(minNodes, maxNodes) {
    myDiagram.startTransaction("generateDigraph");
    // replace the diagram's model's nodeDataArray
    generateNodes(minNodes, maxNodes);
    // replace the diagram's model's linkDataArray
    generateLinks();
    // force a diagram layout
    layout();
    myDiagram.commitTransaction("generateDigraph");
  }

  // Creates a random number of randomly colored nodes.
  function generateNodes(minNodes, maxNodes) {
    var nodeArray = [];
    // get the values from the fields and create a random number of nodes within the range
    var min = parseInt(minNodes, 10);
    var max = parseInt(maxNodes, 10);
    if (isNaN(min)) min = 0;
    if (isNaN(max) || max < min) max = min;
    var numNodes = Math.floor(Math.random() * (max - min + 1)) + min;
    var i;
    for (i = 0; i < numNodes; i++) {
      nodeArray.push({
        key: i,
        text: i.toString(),
        fill: go.Brush.randomColor(),
        size: new go.Size(20 + Math.random() * 50, 20 + Math.random() * 50)
      });
    }

    // randomize the node data
    for (i = 0; i < nodeArray.length; i++) {
      var swap = Math.floor(Math.random() * nodeArray.length);
      var temp = nodeArray[swap];
      nodeArray[swap] = nodeArray[i];
      nodeArray[i] = temp;
    }

    // set the nodeDataArray to this array of objects
    myDiagram.model.nodeDataArray = nodeArray;
  }

  // Create some link data
  function generateLinks() {
    if (myDiagram.nodes.count < 2) return;
    var linkArray = [];
    var nit = myDiagram.nodes;
    var nodes = new go.List();
    nodes.addAll(nit);
    for (var i = 0; i < nodes.count - 1; i++) {
      var from = nodes.elt(i);
      var numto = Math.floor(1 + (Math.random() * 3) / 2);
      for (var j = 0; j < numto; j++) {
        var idx = Math.floor(i + 5 + Math.random() * 10);
        if (idx >= nodes.count) idx = i + (Math.random() * (nodes.count - i)) | 0;
        var to = nodes.elt(idx);
        linkArray.push({ from: from.data.key, to: to.data.key });
      }
    }
    myDiagram.model.linkDataArray = linkArray;
  }

  function layout() {
    myDiagram.startTransaction("change Layout");
    var lay = myDiagram.layout;

    var direction = getRadioValue("direction");
    direction = parseFloat(direction, 10);
    lay.direction = direction;

    var layerSpacing = document.getElementById("layerSpacing").value;
    layerSpacing = parseFloat(layerSpacing, 10);
    lay.layerSpacing = layerSpacing;

    var columnSpacing = document.getElementById("columnSpacing").value;
    columnSpacing = parseFloat(columnSpacing, 10);
    lay.columnSpacing = columnSpacing;

    var cycleRemove = getRadioValue("cycleRemove");
    if (cycleRemove === "CycleDepthFirst") lay.cycleRemoveOption = go.LayeredDigraphLayout.CycleDepthFirst;
    else if (cycleRemove === "CycleGreedy") lay.cycleRemoveOption = go.LayeredDigraphLayout.CycleGreedy;

    var layering = getRadioValue("layering");
    if (layering === "LayerOptimalLinkLength") lay.layeringOption = go.LayeredDigraphLayout.LayerOptimalLinkLength;
    else if (layering === "LayerLongestPathSource") lay.layeringOption = go.LayeredDigraphLayout.LayerLongestPathSource;
    else if (layering === "LayerLongestPathSink") lay.layeringOption = go.LayeredDigraphLayout.LayerLongestPathSink;

    var initialize = getRadioValue("initialize");
    if (initialize === "InitDepthFirstOut") lay.initializeOption = go.LayeredDigraphLayout.InitDepthFirstOut;
    else if (initialize === "InitDepthFirstIn") lay.initializeOption = go.LayeredDigraphLayout.InitDepthFirstIn;
    else if (initialize === "InitNaive") lay.initializeOption = go.LayeredDigraphLayout.InitNaive;

    var aggressive = getRadioValue("aggressive");
    if (aggressive === "AggressiveLess") lay.aggressiveOption = go.LayeredDigraphLayout.AggressiveLess;
    else if (aggressive === "AggressiveNone") lay.aggressiveOption = go.LayeredDigraphLayout.AggressiveNone;
    else if (aggressive === "AggressiveMore") lay.aggressiveOption = go.LayeredDigraphLayout.AggressiveMore;

    //TODO implement pack option
    var pack = document.getElementsByName("pack");
    var packing = 0;
    for (var i = 0; i < pack.length; i++) {
      if (pack[i].checked) packing = packing | parseInt(pack[i].value, 10);
    }
    lay.packOption = packing;

    var setsPortSpots = document.getElementById("setsPortSpots");
    lay.setsPortSpots = setsPortSpots.checked;

    myDiagram.commitTransaction("change Layout");
  }

  function getRadioValue(name) {
    var radio = document.getElementsByName(name);
    for (var i = 0; i < radio.length; i++)
      if (radio[i].checked) return radio[i].value;
  }
  </script>
</head>
<body onload="init()">
<div id="sample">
  <div style="margin-bottom: 5px; padding: 5px; background-color: aliceblue">
    <span style="display: inline-block; vertical-align: top; padding: 5px">
      <b>New Graph</b><br />
      MinNodes: <input type="text" size="2" id="minNodes" value="14" /><br />
      MaxNodes: <input type="text" size="2" id="maxNodes" value="14" /><br />
      <button type="button" onclick="rebuildGraph()">Generate Digraph</button>
    </span>
    <span style="display: inline-block; vertical-align: top; padding: 5px">
      <b>LayeredDigraphLayout Properties</b><br />
      Direction:
      <input type="radio" name="direction" onclick="layout()" value="0" checked="checked" />Right (0)
      <input type="radio" name="direction" onclick="layout()" value="90" />Down (90)
      <input type="radio" name="direction" onclick="layout()" value="180" />Left (180)
      <input type="radio" name="direction" onclick="layout()" value="270" />Up (270)<br />
      LayerSpacing:
      <input type="text" size="2" id="layerSpacing" value="25" onchange="layout()" style="clear: left;" /><br />
      ColumnSpacing:
      <input type="text" size="2" id="columnSpacing" value="25" onchange="layout()" /><br />
      CycleRemove:
      <input type="radio" name="cycleRemove" onclick="layout()" value="CycleDepthFirst" checked="checked" /> CycleDepthFirst
      <input type="radio" name="cycleRemove" onclick="layout()" value="CycleGreedy" /> CycleGreedy<br />
      Layering:
      <input type="radio" name="layering" onclick="layout()" value="LayerOptimalLinkLength" checked="checked" /> LayerOptimalLinkLength
      <input type="radio" name="layering" onclick="layout()" value="LayerLongestPathSource" /> LayerLongestPathSource
      <input type="radio" name="layering" onclick="layout()" value="LayerLongestPathSink" /> LayerLongestPathSink<br />
      Initialize:
      <input type="radio" name="initialize" onclick="layout()" value="InitDepthFirstOut" checked="checked" /> InitDepthFirstOut
      <input type="radio" name="initialize" onclick="layout()" value="InitDepthFirstIn" /> InitDepthFirstIn
      <input type="radio" name="initialize" onclick="layout()" value="InitNaive" /> InitNaive<br />
      Aggressive:
      <input type="radio" name="aggressive" onclick="layout()" value="AggressiveNone" /> AggressiveNone
      <input type="radio" name="aggressive" onclick="layout()" value="AggressiveLess" checked="checked" /> AggressiveLess
      <input type="radio" name="aggressive" onclick="layout()" value="AggressiveMore" /> AggressiveMore<br />
      Pack:
      <input type="checkbox" name="pack" onclick="layout()" value="4" checked="checked" /> PackMedian
      <input type="checkbox" name="pack" onclick="layout()" value="2" checked="checked" /> PackStraighten
      <input type="checkbox" name="pack" onclick="layout()" value="1" checked="checked" /> PackExpand<br />
      SetsPortSpots: <input type="checkbox" id="setsPortSpots" onclick="layout()" value="SetsPortSpots" checked="checked" /><br />
    </span>
  </div>
  <div id="myDiagramDiv" style="border: solid 1px black; background: white; width: 100%; height: 500px" />
</div>
</body>
</html>

It works, thank you!