Mix of ForceDirectedLayout and CircularLayout

Fine!!!

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

  var myModel = $(go.GraphLinksModel);
  myModel.nodeDataArray = [
    { key: "Alpha", isGroup: true },
    { 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"}
    ];

And now i want that the topLeft corner of the circle “center” is on position p.e. 200,200

For convenience, set Group.locationObjectName to “CENTER”. Now the Group.location will be the top-left corner of the circle.

Call Node.move (or Node.moveTo). Since move takes a position, not a location, you have to calculate the point as (200 + group.location.x - group.actualBounds.x, 200 + group.location.y - group.actualBounds.y).

Sorry, what is my problem?

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: $(go.CircularLayout,{
        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: 80, height: 80, fill: "lightblue" },
      ) 
    )  
  );

var myModel = $(go.GraphLinksModel);
myModel.nodeDataArray = [
  { key: "Alpha", isGroup: true },
  { 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;

myDiagram.startTransaction('test2');
group = myDiagram.findNodeForKey("Alpha");
group.move(new go.Point(200 + group.location.x - group.actualBounds.x, 200 + group.location.y - group.actualBounds.y));
myDiagram.commitTransaction('test2');

For one, you are now specifying the locationSpot to be Center when you earlier asked about the top-left spot.

In this example the properties locationSpot and locationObjectName have no effect. Try it please

When debug i get this values, before i move the group;

group.location.x = 0
group.actualBounds.x = 0
group.location.y = 34.xxxx
group.actualBounds.y = -7.xxx

Try removing the locationObjectName, since the locationObject will always be the Placeholder. Then, make sure you are making your move call after the nodes have been postioned by the CircularLayout. Perhaps use an “InitialLayoutCompleted” event listener. The conditions under which you move the group will depend on your app, but you want to make sure the member nodes have been positioned by the layout.

myDiagram.addDiagramListener("InitialLayoutCompleted", function(e) {
  myDiagram.startTransaction('test2');
  var group = myDiagram.findNodeForKey("Alpha");
  group.move(new go.Point(200 + group.location.x - group.actualBounds.x, 200 + group.location.y - group.actualBounds.y));
  myDiagram.commitTransaction('test2');
});

I move the group per button click.

function move() {
  myDiagram.startTransaction('test2');
  group = myDiagram.findNodeForKey("Alpha");
  group.move(new go.Point(200 + group.location.x - group.actualBounds.x, 0 + group.location.y - group.actualBounds.y));
  myDiagram.commitTransaction('test2');  
}
function info() {
  group = myDiagram.findNodeForKey("Alpha");
  alert("Location (x,y) = ("+group.location.x+", "+group.location.y+")");
}

The location of the group is here, before move:

Sorry, but that’s not what I’m seeing. When I click the Info button before moving the group, the location is the middle of the light blue circle.

Could you please try to reproduce the problem with codepen, provide the steps to reproduce it, and explain how you’d like it to work?

Fine it work’s. Thank you!!!

So i move the topLeft-Corner of the ‘center’ circle to my position (here 200,200 - 50 is half of the circle)

  group.move(new go.Point(200 - (group.location.x - group.actualBounds.x) + 50, 200 - (group.location.y - group.actualBounds.y) + 50));

Okay i hope this is the last question:

When i have one member, then the group circle and member circle have the same position:

but i want this:

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.