Different sizes for each band

Hi! Following with the solution of this post How to add an empty band to the layeredDiGraph - #5 by walter I would like to increase band sizes in order to have parents and sons of the same band at different levels. For example, in the following example, i would like to increase band “Two” to have node “fourB” on the right of “threeD”. How would i do that? Thanks in advance

1 Like

So you no longer want to have a one-to-one mapping of layered-digraph layers to diagram bands. I think you can achieve that by assigning “fourB” to its own layer between the layers for “threeD” and “twoD”. Then you need to change the override of LayeredDigraphLayout.commitLayers to pretend that those two layers should go into the same logical band. Basically the band labeled “Two” will need to get the union of the rectangular areas for those two layout layers. And the bands labeled “Three” and “Four” will get the layer bounds assigned to their respective layers. (Which are confusingly numbered by LayeredDigraphLayout as 1 and 0, respectively, although that is hidden from you in the call to commitLayers.)

Thanks a lot, got the idea, but I’m quite newbie in GoJS, any additional input about what and how modify LayeredDigraphLayout.commitLayers in order to stick some layers in the same band would be awesome. Thanks in advance

OK, let’s get rid of that extra data.layer property used by the custom BandedLDLayout in that sample. So the graph gets laid out normally:
image

Now we’ll add a data property indicating that that node/vertex, “fourB”, should go into the band that comes before its normal band:

      { key: "fourB", parent: "threeD", inPreviousBand: true },

With the following changes to BandedLDLayout, we now get:

  // this controls whether the layout is horizontal and the layer bands are vertical, or vice-versa:
  var HORIZONTAL = true;  // this constant parameter can only be set here, not dynamically

  // Perform a LayeredDigraphLayout where commitLayers is overridden to modify the background Part whose key is "_BANDS".
  function BandedLDLayout() {
    go.LayeredDigraphLayout.call(this);
  }
  go.Diagram.inherit(BandedLDLayout, go.LayeredDigraphLayout);

  BandedLDLayout.prototype.assignLayers = function() {
    go.LayeredDigraphLayout.prototype.assignLayers.call(this);
    // now assign specific layers
    var shifted = new Array(this.maxLayer+1);
    var it = this.network.vertexes.iterator;
    while (it.next()) {
      var v = it.value;
      if (v.node !== null && v.node.data.inPreviousBand) {
        shifted[v.layer] = true;
      }
    }
    var shifts = [];
    var c = 0;
    for (var i = 0; i < shifted.length; i++) {
      shifts[i] = c;
      if (shifted[i]) c++;
    }
    it.reset();
    while (it.next()) {
      var v = it.value;
      v.layer += shifts[v.layer];
      if (v.node !== null && v.node.data.inPreviousBand) {
        v.layer++;
      }
    }
    var extras = [false];
    for (var i = 1; i < shifts.length; i++) {
      extras[i] = shifts[i] > shifts[i-1];
    }
    this._extras = extras.reverse();
  };

  BandedLDLayout.prototype.commitLayers = function(layerRects, offset) {
    // update the background object holding the visual "bands"
    var bands = this.diagram.findPartForKey("_BANDS");
    if (bands) {
      var model = this.diagram.model;
      bands.location = this.arrangementOrigin.copy().add(offset);

      // set the bounds of each band via data binding of the "bounds" property
      var arr = bands.data.itemArray;
      var j = 0;
      for (var i = 0; i < layerRects.length; i++) {
        var itemdata = arr[j++];
        if (itemdata) {
          var r = layerRects[i];
          if (this._extras[i]) {
            i++;
            r = r.copy().unionRect(layerRects[i]);
          }
          model.setDataProperty(itemdata, "bounds", r);
        }
      }

      // make each band visible or not, depending on whether there is a layer for it
      for (var it = bands.elements; it.next(); ) {
        var idx = it.key;
        var elt = it.value;  // the item panel representing a band
        elt.visible = idx < j;
      }      
    }
  };
  // end BandedLDLayout


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

    myDiagram = $(go.Diagram, "myDiagramDiv",
                  {
                    layout: $(BandedLDLayout,
                              {
                                direction: HORIZONTAL ? 0 : 90
                              }),  // custom layout is defined above
                    "undoManager.isEnabled": true
                  });

    myDiagram.nodeTemplate =
      $(go.Node, go.Panel.Auto,
        $(go.Shape, "Rectangle",
          { fill: "white" }),
        $(go.TextBlock, { margin: 5 },
          new go.Binding("text", "key")));

    // There should be at most a single object of this category.
    // This Part will be modified by BandedLDLayout.commitLayers to display visual "bands"
    // where each "layer" is a layer of the tree.
    // This template is parameterized at load time by the HORIZONTAL parameter.
    // You also have the option of showing rectangles for the layer bands or
    // of showing separator lines between the layers, but not both at the same time,
    // by commenting in/out the indicated code.
    myDiagram.nodeTemplateMap.add("Bands",
      $(go.Part, "Position",
        new go.Binding("itemArray"),
        {
          isLayoutPositioned: false,  // but still in document bounds
          locationSpot: new go.Spot(0, 0, HORIZONTAL ? 0 : 16, HORIZONTAL ? 16 : 0),  // account for header height
          layerName: "Background",
          pickable: false,
          selectable: false,
          itemTemplate:
            $(go.Panel, HORIZONTAL ? "Vertical" : "Horizontal",
              new go.Binding("position", "bounds", function(b) { return b.position; }),
              $(go.TextBlock,
                {
                  angle: HORIZONTAL ? 0 : 270,
                  textAlign: "center",
                  wrap: go.TextBlock.None,
                  font: "bold 11pt sans-serif",
                  background: $(go.Brush, "Linear", { 0: "aqua", 1: go.Brush.darken("aqua") })
                },
                new go.Binding("text"),
                // always bind "width" because the angle does the rotation
                new go.Binding("width", "bounds", function(r) { return HORIZONTAL ? r.width : r.height; })
              ),
              $(go.Shape,
                { stroke: null, strokeWidth: 0 },
                new go.Binding("desiredSize", "bounds", function(r) { return r.size; }),
                new go.Binding("fill", "itemIndex", function(i) { return i % 2 == 0 ? "whitesmoke" : go.Brush.darken("whitesmoke"); }).ofObject())
            )
        }
      ));

    myDiagram.linkTemplate =
      $(go.Link,
        $(go.Shape));  // simple black line, no arrowhead needed

    // define the tree node data
    var nodearray = [
      { // this is the information needed for the headers of the bands
        key: "_BANDS",
        category: "Bands",
        itemArray: [
          { text: "Zero" },
          { text: "One" },
          { text: "Two" },
          { text: "Three" },
          { text: "Four" },
          { text: "Five" }
        ]
      },
      // these are the regular nodes in the TreeModel
      { key: "root" },
      { key: "oneB", parent: "root" },
      { key: "twoA", parent: "oneB" },
      { key: "twoC", parent: "root" },
      { key: "threeC", parent: "twoC" },
      { key: "threeD", parent: "twoC" },
      { key: "fourB", parent: "threeD", inPreviousBand: true },
      { key: "fourC", parent: "threeC" },
      { key: "fourD", parent: "fourB" },
      { key: "twoD", parent: "root" }
    ];

    myDiagram.model = new go.TreeModel(nodearray);
  }

Note that if a node/vertex is the only one in its band and it is moved up to the previous band, the old band will be empty. By default, as you can see in the code (unchanged from the sample), it makes empty bands invisible. So in the original sample, node “fourC” was not a child of “threeC” but of “twoC”. I moved it so that the band would not become empty. Making that band empty would cause it to become invisible, resulting in “Two” and “Four” adjoining each other. You might want to do something different.

What a master class!Thanks a lot!

You’re welcome. It occurs to me now that the right thing to do, when “moving” a node/vertex to the previous band, is to move over any dependent nodes. For the case of “fourB”, it meant that “fourD” could have been moved too, into the layer holding “fourC”. Then if there hadn’t been any other node in that band, it wouldn’t be empty, so there wouldn’t be a problem with that band becoming invisible. Oh, well, I don’t have time now to fix that, but ideally that is what should be done.

Im finding a final problem. It only allows me to have two “levels” at the same band. For example, in the example you posted, it doesn’t work if I want to move fourD to Band “Two”. How would I allow that?

Thank you very much

If you had specified that when you described what you wanted to do, I might have implemented that. I’m a bit busy now, but if you purchase a license with support, I can implement what you need, once you describe it well enough for me to understand.