Covering Tree Layout with custom width

Hi,
I am using coving tree layout in my diagram. Where the parent width is equal to the subtree width size.

refer - Bind Parent Node Width to Width of All Child Nodes

I am running into a corner case issue where if the width of the parent node is greater than the subtree size, the text is wrapped.

image

Ideally I want the parent node to set Default width without wrapping
i.e. -

if (parentNode.width > subtreeSize.width)
return parentNode.width;
else
parentNode.width = subtreeSize.width;

expected -
image

I tried using GraphObject.minSize but it works only for the fixed dimensions set.

Here’s the code for coveringTreeLayout

 function CoveringTreeLayout() {
    go.TreeLayout.call(this);
    this.compaction = go.TreeLayout.CompactionNone;  // necessary to avoid potential overlapping nodes
    this.alignment = go.TreeLayout.AlignmentStart;
    this.alternateAlignment = go.TreeLayout.AlignmentStart;
  }
  go.Diagram.inherit(CoveringTreeLayout, go.TreeLayout);

  CoveringTreeLayout.prototype.assignTreeVertexValues = function(v) {
    if (v.childrenCount > 0) {
      if (this.angle === 0 || this.angle === 180) {
        v.height = 0;
      } else {
        v.width = 0;
      }
    }
  };

  CoveringTreeLayout.prototype.commitNodes = function() {
    var vit = this.network.vertexes.iterator;
    while (vit.next()) {
      var v = vit.value;
      var n = v.node;
      if (n === null) continue;
      n.position = new go.Point(v.x, v.y);
      if (v.childrenCount > 0) {
        if (this.angle === 0 || this.angle === 180) {
          n.height = v.subtreeSize.height;
        } else {
          n.width = v.subtreeSize.width;
        }
      }
    }
  };

Thanks in advance

Does the following CoveringTreeLayout work as you’d expect?

// Change the size of each parent node to be broad enough to cover all of its children
class CoveringTreeLayout extends go.TreeLayout {
  constructor() {
    super();
    this.compaction = go.TreeLayout.CompactionNone;  // necessary to avoid potential overlapping nodes
    this.alignment = go.TreeLayout.AlignmentStart;
    this.alternateAlignment = go.TreeLayout.AlignmentStart;
  }

  commitNodes() {
    var vit = this.network.vertexes.iterator;
    while (vit.next()) {
      var v = vit.value;
      var n = v.node;
      if (n === null) continue;
      if (v.childrenCount > 0) {
        if (this.angle === 0 || this.angle === 180) {
          n.height = Math.max(n.actualBounds.height, v.subtreeSize.height);
        } else {
          n.width = Math.max(n.actualBounds.width, v.subtreeSize.width);
        }
      }
    }
    super.commitNodes();
  }
}
// end CoveringTreeLayout

It can be seen here: CoveringTreeLayout

Notice that “Alpha1” retains its size instead of becoming the size of its smaller subtree.

Hi,
Thanks for your reply
I implemented your changes by comparing actualBound to the subtreeSize and it still wraps the text.
Also the alignment of the link to the childnode is displaced.
image

Thanks

How is your node template defined?

Do you want the child node to grow to the size of the parent if the parent is larger? Or to be centered under the parent?

Hi,
In my diagram, I have the parent level nodes to which child nodes are linked through straight arrows.
Using the covering tree layout I am able to set parent node width to the size of the subtree
For straight orthogonal lines to the center of the child node are set using a custom class - Barlink

<!DOCTYPE html>
<html>
<head>
  <title>Minimal GoJS Sample</title>
  <!-- Copyright 1998-2022 by Northwoods Software Corporation. -->
</head>
<body>
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>

  <script src="https://unpkg.com/gojs"></script>
  <script id="code">

// This custom Link class is smart about computing the link point and direction
class BarLink extends go.Link {
  getLinkPoint(node, port, spot, from, ortho, othernode, otherport) {
    const r = port.getDocumentBounds();
    const op = otherport.getDocumentBounds();
    const below = op.centerY > r.centerY;
    const y = below ? r.bottom : r.top;
    if (op.right < r.left) return new go.Point(r.left, y);
    if (op.left > r.right) return new go.Point(r.right, y);
    return new go.Point((Math.max(r.left, op.left) + Math.min(r.right, op.right))/2, y);
  }

  getLinkDirection(node, port, linkpoint, spot, from, ortho, othernode, otherport) {
    const p = port.getDocumentPoint(go.Spot.Center);
    const op = otherport.getDocumentPoint(go.Spot.Center);
    const below = op.y > p.y;
    return below ? 90 : 270;
  }
}
// end BarLink class

const $ = go.GraphObject.make;

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
    {
      layout:
        $(go.TreeLayout,
          {
            angle: 90,
            setsPortSpot: false,
            setsChildPortSpot: false,
            arrangement: go.TreeLayout.ArrangementHorizontal
          }),
      "undoManager.isEnabled": true
    });

myDiagram.nodeTemplate =
  $(go.Node, "Auto",
    $(go.Shape,
      { fill: "white" },
      new go.Binding("fill", "color")),
    $(go.TextBlock,
      { margin: 8, editable: true },
      new go.Binding("text").makeTwoWay())
  );

myDiagram.linkTemplate =
  $(BarLink,
    { routing: go.Link.Orthogonal },
    $(go.Shape),
    $(go.Shape, { toArrow: "Standard" })
  );

myDiagram.linkTemplateMap.add("Seq",
  $(go.Link,
    { isLayoutPositioned: false, isTreeLink: false },
    $(go.Shape),
    $(go.Shape, { toArrow: "OpenTriangle" })
  ));

myDiagram.model = new go.GraphLinksModel(
[
  { key: 0, text: "Start" },
  { 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: "Alpha1", color: "lightblue" },
  { key: 12, text: "Beta1", color: "orange" },
  { key: 13, text: "Gamma1", color: "lightgreen" },
  { key: 21, text: "Alpha2", color: "lightblue" },
  { key: 22, text: "Beta2", color: "orange" },
  { key: 23, text: "Gamma2", color: "lightgreen" },
  { key: 24, text: "Delta3", color: "pink" },
  { key: 25, text: "Epsilon3", color: "yellow" },
  { key: 31, text: "Alpha3", color: "lightblue" },
  { key: 32, text: "Beta3", color: "orange" },
  { key: 33, text: "Gamma3", color: "lightgreen" },
  { key: 99, text: "End" }
],
[
  { from: 0, to: 1, category: "Seq" },
  { from: 1, to: 2 },
  { from: 1, to: 3 },
  { from: 3, to: 4 },
  { from: 1, to: 11, category: "Seq" },
  { from: 11, to: 12 },
  { from: 11, to: 13 },
  { from: 11, to: 21, category: "Seq" },
  { from: 21, to: 22 },
  { from: 21, to: 23 },
  { from: 22, to: 24 },
  { from: 24, to: 25 },
  { from: 21, to: 31, category: "Seq" },
  { from: 31, to: 32 },
  { from: 31, to: 33 },
  { from: 31, to: 99, category: "Seq" }
]);
  </script>
</body>
</html>

As shown
image

But in some cases where the parent node text length is longer, it wraps the text as so -
image
to avoid it - as you mentioned I tried to set the node width by comparing actual bounds to subtree width, but by doing so, the wrap text issue is not resolved. In addition the link alignment is improper -

Hope this helps
Thanks

If you look at CoveringTreeLayout, you will see that when the root node is wider than any of its descendant layers, the root will be wider than the subtree.

So I think you just need to change your node template so that it isn’t naturally limited in width.