Hi,
the layout (GoLayoutLayeredDigraph) would allow overlapping of the text.
Hi,
It’s probably easiest to just treat each link label as if they were “nodes” in their own right. So, something like:
[code] GoLayoutLayeredDigraph layout = new GoLayoutLayeredDigraph();
layout.Document = myView.Document;
// create a normal network …
GoLayoutLayeredDigraphNetwork net = layout.CreateNetwork();
net.AddNodesAndLinksFromCollection(myView.Document, true);
// but then add fake nodes for the link labels
foreach (GoObject obj in myView.Document) {
GoLabeledLink ll = obj as GoLabeledLink;
if (ll != null && ll.MidLabel != null && !ll.RealLink.Orthogonal) {
GoLayoutLayeredDigraphLink nl = net.FindLink(ll);
if (nl == null) continue;
ll.Initializing = true;
GoLayoutLayeredDigraphNode from = nl.FromNode;
GoLayoutLayeredDigraphNode to = nl.ToNode;
GoLayoutLayeredDigraphNode nn = net.AddNode(ll.MidLabel);
net.LinkNodes(from, nn, null);
net.LinkNodes(nn, to, null);
net.DeleteLink(nl);
}
}
layout.Network = net;
layout.PerformLayout();
// now update the stroke path for the links, to position the labels
foreach (GoObject obj in myView.Document) {
GoLabeledLink ll = obj as GoLabeledLink;
if (ll != null && ll.MidLabel != null && !ll.RealLink.Orthogonal) {
int numpts = ll.RealLink.PointsCount;
ll.RealLink.InsertPoint(numpts/2, ll.MidLabel.Center);
GoPort p = ll.FromPort as GoPort;
if (p != null)
ll.RealLink.SetPoint(0, p.GetFromLinkPoint(ll));
p = ll.ToPort as GoPort;
if (p != null)
ll.RealLink.SetPoint(ll.RealLink.PointsCount-1, p.GetToLinkPoint(ll));
ll.Initializing = false;
}
}[/code]
Note: this code just works for “direct” links, not for Bezier style nor for Orthogonal links.
Cool.
Thanks for this code.
I will try it!
This works well. Thank you.
The label automatically gets repositioned as either node is moved. This is because GoLabeledLink.LayoutChildren will call GoLabeledLink.LayoutMidLabel which will call GoLabeledLink.PositionMidLabel.
Or are you saying this is not what is happening?
No, it is not happening correctly. (I will send details and screen shots later…)
images
You understand why it’s doing what it’s doing, right? There’s a link from “Loc1” to “fg1”, so naturally it assumed that “fg1” should be in a layer after the “Loc1” layer. The same is true for “Loc2” and its “fg1”.
And if you just removed the link from “Loc1” to “fg1”, but left the link from “fg1” to “Loc1” in the network, then it would naturally think that “fg1” ought to be in a layer before the “Loc1” layer.
So you need to replace both of the GoLayoutLayeredDigraphLinks connecting the GoLayoutLayeredDigraphNodes corresponding to “Loc1” and “fg1”, with new GoLayoutLayeredDigraphLinks that connect “Sup1” to “fg1” and “fg1” to “Loc2”. (And of course the same goes for the two links connecting “Loc2” with its “fg1”.) You can use GoLayoutLayeredDigraphNetwork methods to accomplish this. But there won’t be any guarantee that the “Loc1” and its “fg1” nodes will be next to each other in the layer. (Of course if there were more than two such nodes, that would be impossible anyway.)
Thanks for the explanation. I now have a pretty good idea how the node layout positions are calculated. (and no; I did not understand this properly before - thanks again.)
OK, here’s one way to associate one node with another node on the same layer. Just ignore that extra “fg” node, and pretend the “Loc” node is taller than it really is, to make room for positioning the “fg” node afterwards.
[code] public class TDKLayout : GoLayoutLayeredDigraph {
public override void PerformLayout() {
GoLayoutLayeredDigraphNetwork net = this.Network;
if (net == null) net = new GoLayoutLayeredDigraphNetwork(this.Document);
foreach (GoObject obj in this.Document) {
GoTextNode tn = obj as GoTextNode;
if (tn == null) continue;
if (tn.UserObject == null) continue;
GoTextNode near = (GoTextNode)tn.UserObject;
// delete all of those “fg” nodes from the network
net.DeleteNode(tn);
// increase the height of the “Loc” nodes to include the “fg” node
GoLayoutLayeredDigraphNode nearnode = net.FindNode(near);
if (nearnode != null) {
RectangleF bounds = near.Bounds;
bounds.Height += 20 + tn.Height;
nearnode.Bounds = bounds;
}
}
this.Network = net;
base.PerformLayout();
// now position all of those “fg” nodes
foreach (GoObject obj in this.Document) {
GoTextNode tn = obj as GoTextNode;
if (tn == null) continue;
if (tn.UserObject == null) continue;
GoTextNode near = (GoTextNode)tn.UserObject;
tn.SetSpotLocation(GoObject.MiddleTop, near, GoObject.MiddleBottom, 0, 20);
}
}
// to keep your code better organized, you'll probably want to put
// that other code for adding fake nodes for MidLabels, and for
// positioning those MidLabels afterwards, into the method above.
// just some test case initialization code...
public static void InitDoc(GoDocument doc) {
GoTextNode sup1 = new GoTextNode();
sup1.Text = "Sup1";
doc.Add(sup1);
GoTextNode loc1 = new GoTextNode();
loc1.Text = "Loc1";
doc.Add(loc1);
GoTextNode loc2 = new GoTextNode();
loc2.Text = "Loc2";
doc.Add(loc2);
GoTextNode dploc2 = new GoTextNode();
dploc2.Text = "DPLoc2";
doc.Add(dploc2);
GoTextNode fg1 = new GoTextNode();
fg1.Text = "fg1";
fg1.UserObject = loc1;
doc.Add(fg1);
GoTextNode fg2 = new GoTextNode();
fg2.Text = "fg2";
fg2.UserObject = loc2;
doc.Add(fg2);
doc.Add(MakeLink(sup1.RightPort, loc1.LeftPort));
doc.Add(MakeLink(loc1.RightPort, loc2.LeftPort));
doc.Add(MakeLink(loc2.RightPort, dploc2.LeftPort));
doc.Add(MakeLink(loc1.BottomPort, fg1.LeftPort));
doc.Add(MakeLink(fg1.RightPort, loc1.BottomPort));
doc.Add(MakeLink(loc2.BottomPort, fg2.LeftPort));
doc.Add(MakeLink(fg2.RightPort, loc2.BottomPort));
}
public static GoLink MakeLink(GoPort a, GoPort b) {
GoLink l = new GoLink();
l.ToArrow = true;
l.Style = GoStrokeStyle.Bezier;
l.FromPort = a;
l.ToPort = b;
return l;
}
}[/code]
Usage:
TDKLayout.InitDoc(doc); // create test case
TDKLayout layout = new TDKLayout();
layout.Document = doc;
layout.PerformLayout();
Result in version 3.0:
You might get different (vertical) alignment in version 2.x in more complicated cases than this simple one. (Sorry, I haven’t tried it in v2.6.)
Of course you can extend this to support additional associated nodes like the “fg” ones for each regular node like “Loc”. You’ll need to decide where to position those nodes relative to the main node.
Also, if you are using GoTextNode, remember that if you position a “fg” node above its “Loc” node, to avoid having the two connecting links cross over the “Loc” node, you’ll probably want to reconnect the two links so that they connect to the “Loc” node’s TopPort instead of its BottomPort as shown in the above example.
You have clearly spent some time on this! Thank you!
Extending the bounds of a node on the network (to fit another node below the original) sounds asif the layout would use the extended bounds and draw the next line beyond those bounds. However …
Does the code I posted work for you? I just tried several variations of the graph, in version 2.6.2, and it produced good results for all cases.
It works.