About ForceDirectedLayout

Q1: Is the Layout the value of a Group.layout? If so, does that Group have a Placeholder, as the value of Group.placeholder?

In that case the size of the Group will depend on the area covered by its members. But the layout is responsible for moving those member nodes, so the layout should know the area that its nodes are being laid out in. Thus it should not be necessary to ask the Group how big it is before the layout has moved the nodes, and in fact it is misleading to depend on any information about the group’s size while the layout is happening.

Q2: If you are using ForceDirectedLayout, I think one problem is that the layout thinks that links connecting to member nodes of a group think it is just connecting with the group as a whole, not to a particular member node. So it does not realize that a link connects to one particular side of the group – it thinks they are all the same.

I"m wondering if you can get better results for this situation by setting Group.layout to null, so that the ForceDirectedLayout will operate on all of the nodes and links while completely ignoring Groups. But presumably you want each Group to layout its nodes inside itself, which is why you have that SquareLayout, so you cannot set Group.layout to null.

You could customize the ForceDirectedLayout to ignore all of the groups but treat each node within groups as fixed, so that they are not moved. Sorry, but I don’t have time now to work on this for you.

Q3: All that information should be in the Node.data object’s properties.

Thank you very much for your answer.I benefited a lot.

ForceDirectedLayout is a random location assignment.Can I make it like TreeLayout and layeredDigraphLayout, when I assign the next node location, there’s a direction that makes the toNode on the left, or the right, or the top, or the bottom?
In other words, the direction of the link can determine the location of toNode in a certain direction.

I’m not sure ForceDirectedLayout can be that specific. Remember, it is supposed to simulate a physical system of springs and electrical charges and gravity. A Spring (acting for a Link) does not normally have any absolute direction associated with its forces.

Oh, thank you very much.I have another question :I don’t want links to crossing the group, and I hope that go.Link.AvoidsNodes can also avoid group, not just node.What should I do?

Is each such link crossing over a Group because it is connected with a Node that is inside a Group?

You could try setting Group.avoidable to true, although that would mean that links that were completely unrelated to a group might cross over that group, but without crossing over any of its regular member nodes.

Oops, I see I wrote the wrong thing in my previous reply. I meant to say:

You could try setting Group.avoidable to false, although that would mean that links that were completely unrelated to a group might cross over that group, …

Q1:If links are related to a group?What to do about it?Group.avoidable to false is invalid.
Q2:How does links not overlap?

As shown in the figure, if there is no decoration, I can’t clearly distinguish between the from and to of the link.


In your sketch, are those regular nodes? The common solution is to use “Side…” spots. GoJS Link Connection Points on Nodes -- Northwoods Software
Entity Relationship

But if the links are connecting with those tiny red nodes inside your groups, then I don’t see any choices for you. The links have to go to or from those small red nodes.

A post was split to a new topic: Can there be multiple links between nodes?

My link codes:

           g_(go.Link, {
	          selectionAdorned: true,
	          reshapable: true,
	          routing: go.Link.AvoidsNodes,
	          corner: 5,
	          curve: go.Link.JumpOver,
                 toShortLength: 8,
                 resegmentable: true,
                 relinkableFrom: true,
                relinkableTo: true
	        },new go.Binding("points").makeTwoWay(),
	        g_(go.Shape,  // the link shape
	          { stroke: "#303B45", strokeWidth: 2.5 },
	            new go.Binding("strokeWidth","thickness"), 
	            new go.Binding("strokeDashArray","dashes"), 
	            new go.Binding("stroke","link_color")),
	        g_(go.Shape, { fromArrow: "Block",stroke: "#f00",fill: "rgba(255,0,0,1)" }),
	        g_(go.Shape,{ toArrow: "Block",stroke: "#f00",fill: "rgba(255,0,0,1)" }),
	          g_(go.TextBlock, // the "from" label
	            alignmentFocus: new go.Spot(0, 0, -10, 20), 
	            segmentOrientation: go.Link.OrientUpright
	          new go.Binding("segmentOffset", "segmentOffset", go.Point.parse).makeTwoWay(go.Point.stringify), 
	          new go.Binding("font","font"), 
	          new go.Binding("stroke","text_color"), 
	          new go.Binding("text","fromText").makeTwoWay()),
	          g_(go.TextBlock,  // the "to" label
	            segmentIndex: -1,
	            alignmentFocus: new go.Spot(0, 0, 30, 20),
	            segmentOrientation: go.Link.OrientUpright
	          new go.Binding("segmentOffset", "segmentOffset", go.Point.parse).makeTwoWay(go.Point.stringify), 
	          new go.Binding("font","font"), 
	          new go.Binding("stroke","text_color"), 
	          new go.Binding("text","toText").makeTwoWay())

My node codes:

var gnTemplate =
g_(go.Node, "Auto",   
    { selectionAdorned: true,
      resizable: true,
      resizeObjectName: "SHAPE",
      rotatable: true,
      portId: "", cursor: "pointer",
      fromLinkableDuplicates: true, 
      toLinkableDuplicates: true,
      fromLinkable: true,
      toLinkable: true,
      fromSpot: go.Spot.AllSides,
      toSpot: go.Spot.AllSides 
      }, new go.Binding("angle","angle").makeTwoWay(), 
      new go.Binding("category","category").makeTwoWay(), 
      new go.Binding("location","loc",go.Point.parse).makeTwoWay(go.Point.stringify), 
      g_(go.Shape, "Rectangle",
      { name: "SHAPE",
    	fill: null, 
    	stroke: "#ff0000", 
    	strokeWidth: 3 ,
    	desiredSize: new go.Size(200, 200)
      new go.Binding("stroke","color"), 
      new go.Binding("strokeWidth","thickness"), 
      new go.Binding("desiredSize","desiredSize",go.Size.parse).makeTwoWay(go.Size.stringify)),
    	  editable: true,
    	  _isNodeLabel: true,
    	  alignment: new go.Spot(0.5,0.5,0,0),
          //margin: new go.Margin(0, 14, 0, 2),  
          font: "bold 16px sans-serif"
        new go.Binding("alignment","alignment").makeTwoWay(), 
        new go.Binding("font","font"), 
        new go.Binding("stroke","color"), 
        new go.Binding("text","text").makeTwoWay()

Q1:As is shown in,when should the link be overlapping and when should it be separated? Can it be controlled by the from/toText of link?

So your ports do use “…Side” spots. So the situation that you are pointing out is where the node at the upper left corner of your screenshot has moved relative to the node at the middle bottom of your screenshot.

The links connecting with the bottom node had been spread out evenly, but then when the upper node or the node at the top-right corner was moved, the routes of links not connecting with the moved node were not re-routed to be spaced nicely at that bottom node.

You can see this behavior in the Entity Relationship sample or any other sample where the spots are AllSides.

The reason for that behavior is that moving one node shouldn’t cause rerouting of unrelated links. After all – the user might have manually reshaped the routes of those links, and we don’t want to lose that state unless it’s unavoidable, as it is for links connecting with the node that is moved.

But you can call Link.invalidateRoute on those other links if you want them to be routed more nicely on the sides of nodes.

Could you be specific about exactly how many nodes you are talking about, and how many ports per node?

In general the presence or absence of link labels at the ends of links does not affect link routing.

If the port number of a node is not determined, it is first created, and then how many nodes are connected to it to create port?

I’m sorry, but I cannot unambiguously parse your post.

I know English is not your native language, but it is not my native language either, so everyone needs to write precisely and carefully to make sure they are understood.

I’m sorry, my English is terrible.
Thank you very much for your patience.
I am not sure about the number of ports per node. Can I create them automatically?
Or does the number of ports per node have to be written in the nodeTemplate?

Actually, your English is pretty good. It’s the complexity of the problem domain and the software that make it hard to communicate.

Have you read GoJS Ports in Nodes-- Northwoods Software ?

It isn’t clear to me that you need more than one port per node.

I used ForceDirectedLayout to get the results:

There are two nodes that are far away because there is no link connection.
Q1:I want these two nodes to be closer to the other nodes.Could you give me some advice?
Q2:Computations stop when no vertex moves more than epsilonDistance or when maxIterations have happened.Can I let the computations never stop?

[EDIT: ArrangingForceDirectedLayout has been subsumed by the ArrangingLayout extension in v2.1.23.]

I suggest that you use the ArrangingLayout extension.
It is demonstrated at: Arranging Layout of the Class Hierarchy
It is defined at: https://gojs.net/latest/extensions/ArrangingLayout.js
It is documented at: ArrangingLayout | GoJS API

In your case you’ll want to set ArrangingLayout.primaryLayout to be your ForceDirectedLayout. Disconnected nodes it will automatically arrange along the bottom of the document by a GridLayout.

The ArrangingForceDirectedLayout is what most people want.

But an alternative design is to customize the ForceDirectedLayout so that there are pretend links/edges between the single (disconnected) nodes/vertexes and other already-connected nodes/vertexes.

I notice now that I didn’t answer your second question. There are a number of possibliities. Perhaps you would be interested in Interactive Force Directed Layout ?