Mix of ForceDirectedLayout and CircularLayout

Yes, as I said in Mix of ForceDirectedLayout and CircularLayout - #5 by walter, when there are zero, one, two, or three member/child nodes you may need to compute their positions specially.

5 posts were split to a new topic: Collapsing and Expanding a Group using CircularLayout does not remain at same location

When i have one member, then the group circle and member circle have the same position, so i try this but it doesn’t work.

DemoCircularLayout.prototype.makeNetwork = function(coll) {
    // call base method for standard behavior
    var net = go.CircularLayout.prototype.makeNetwork.call(this, coll);
    if (net.vertexes.count === 1) {
      net.vertexes.each(function(vertex) {
        vertex.focusX = vertex.focusX -100;
      });
    }
    return net;
};

If you wanted to move a vertex before the layout happens, you would want to shift the value of LayoutVertex.center.

But I don’t think trying to shift it before the layout is what you want to do. You want to override Layout.commitLayout, or commitNodes if such a method exists on the layout that you are using, which in this case it does.

that doesn’t work too… What is my problem?

DemoCircularLayout.prototype.commitNodes = function() {
    // call base method for standard behavior
    var net = go.CircularLayout.prototype.commitNodes.call(this);
    if (this.network.vertexes.count === 1) {
      this.network.vertexes.each(function(vertex) {
        vertex.focusX = vertex.focusX-100;
      });
    }
};

You need to set the LayoutVertex .x & .y, or .centerX & .centerY, before you call the super method.

OR, you can set the Node.position or Node.location after you call the super method.

All possibilities do not work… please try it:

function DemoCircularLayout() {
    go.CircularLayout.call(this);
}
go.Diagram.inherit(DemoCircularLayout, go.CircularLayout);

DemoCircularLayout.prototype.commitNodes = function() {
    // go.CircularLayout.prototype.commitNodes.call(this);
    if (this.network.vertexes.count === 1) {
      this.network.vertexes.each(function(vertex) {
        vertex.positionX = vertex.positionX - 100;
        // var node = vertex.node;
        // if (node) {
        //   node.position = new go.Point(node.position.x - 200, node.position.y);
        // }
      });
    }
    go.CircularLayout.prototype.commitNodes.call(this);
};

var $ = go.GraphObject.make;
var myDiagram =
    $(go.Diagram, "myDiagramDiv",
        {
          initialPosition : new go.Point(-100, -100)
        }
     );
myDiagram.grid.visible = true;

myDiagram.nodeTemplate =
    $(go.Node, 
    {
      background: 'green' },
    $(go.Shape, 'Circle', { width: 40, height: 40, fill: 'red' })
  );

myDiagram.groupTemplate =
  $(go.Group, 'Spot',
    { 
      background: 'rgba(0,255,0,.3)',
      layout: $(DemoCircularLayout, {
        spacing: NaN,
        radius: 100
      }),
      locationSpot: go.Spot.Center,
      locationObjectName: 'CENTER',
    },
    $(go.Placeholder, {}),
    $(go.Panel, "Vertical", { alignmentFocusName: 'CENTER' },
      $(go.TextBlock,
        new go.Binding("text", "key")
      ),
      $(go.Shape, "Circle",
        { name: 'CENTER', portId: "", width: 60, height: 60, fill: "lightblue" },
      ),
      $("SubGraphExpanderButton") 
    )  
  );

var myModel = $(go.GraphLinksModel);
myModel.nodeDataArray = [
  { key: "Alpha", isGroup: true, loc: new go.Point(200, 200)},
  { key: "Beta", group: "Alpha" },
  // { key: "Gamma", group: "Alpha" }
  // { key: "Delta", group: "Alpha" },
  // { key: "Epsilon", group: "Alpha" },
];

myModel.linkDataArray = [
    // { from: "Alpha", to: "Beta"},
    // { from: "Alpha", to: "Gamma"},
    // { from: "Alpha", to: "Delta"},
    // { from: "Alpha", to: "Epsilon"}
  ];

myDiagram.model = myModel;

Sorry, I typed the wrong property names – I have fixed my previous reply.

But it doesn’t work!!! Try the code…

Actually, I think it does work, but perhaps the result is not what you expected.

Your Group template uses a Placeholder, and that Placeholder will occupy the area that the member nodes occupy, in both size and position. So when there is just one member node, as in your case this time, the Placeholder will be just that size. So the Group will just be the size it needs to be, because the Placeholder is actually smaller than the Vertical Panel in the Group template.

I tried your code with two, three, and four member nodes, and the results look good. (Yes, I know about the shifting after collapse/expand, but that’s a different topic, don’t you agree? I’m still looking into it.)

So you need to decide what to do when there is exactly one member node. Perhaps you shouldn’t bother to declare the “parent” node to be a Group if there is less than two child nodes.

Optional discussion that does not apply if you are going to have Diagram.layout be a ForceDirectedLayout or any other predefined Layout subclass:

But you ask – why is the member node where it is? The Group’s position is at (0,0)! That’s because the Diagram.layout, which in this case is an instance of Layout, positioned the Group to be at (0,0) because it didn’t have a real location. If the Diagram.layout were a GridLayout or a TreeLayout, the Group would be positioned elsewhere according to the layout’s rules.

So if you really want to keep the natural positioning of the member nodes, you cannot set Diagram.layout (well, you could set it to another instance of the base Layout class). And you have to make sure that it has a real location so that the Layout.doLayout method doesn’t assign the group’s position anyway. So just set Group.location to new go.Point().

This last step seems counterintuitive – why bother to give a group a real position or a location when the group is just going to go to wherever the member nodes are? The reason is that that is just how the default Layout class works – any Node without a real position or location gets positioned in order to give it a real position and location.