Node overlap in layout

We are using layered digraph layout for creating a subset of nodes in the diagram and after creation it looks something like this



Is it possible to avoid overlap of nodes in this example M overlaps 106 (which is a group), can this be avoided during layout.

Do the 106 and 55 groups have their own Group.Layout?

No, groups do not have any layout.

Is it the case that the second C is linked to B? Nothing connects with the group 106?

I suspect that the layout doesn’t know about the groups at all. It is just positioning the nodes and the groups end up wherever their member nodes get placed collectively. I don’t think there’s an easy solution that will work in general. One possibility is to change the LayeredDigraphNetwork so that links crossing group boundaries are replaced by links connecting the groups, rather than connecting the individual nodes within those groups.

Yes, Second C links to B and none of the Groups are connected, groups just act as a container to contain certain nodes and apart from that they are not used in any other way.



How do I make layout aware of the Groups?

OR

How do I manipulate the LayeredDigraphNetwork, is there any sample in the startup kit that shows this?

Well, you can easily define your own custom LayeredDigraphLayout that overrides the MakeNetwork method.

I’m not familiar with what kinds of graphs you will really have, so I don’t know if it is better to modify the standard network, or to create it from scratch. In the former case you just call the base MakeNetwork method, modify it appropriately, and then return it. In the latter case you don’t bother calling the base method, but just create a LayeredDigraphNetwork and build up the LayeredDigraphVertexes and LayeredDigraphEdges as desired.

The closest example that I can find in version 1.2 is how the FDLayout sample customizes the ForceDirectedNetwork. But it doesn’t modify the graph – it just sets a property of the ForceDirectedVertexes based on a property of the corresponding Node.

To decide if a given Link crosses a Group boundary, compare the Link.FromNode.ContainingSubGraph with the Link.ToNode.ContainingSubGraph.

When you say



change the LayeredDigraphNetwork so that links crossing group boundaries are replaced by links connecting the groups, rather than connecting the individual nodes within those groupss”



you mean to do this by using DeleteEdge and AddEdge methods and not by directly changing the model?



If yes, then I should DeleteEdge(55-C > 106-B) and then AddEdge(55 > 106)

how do i get vertex for groups?

I think that’s right. You may need to experiment with different replacement policies to see what works for the kinds of graphs you have.

FindVertex ought to work.

Ok, connecting the groups has stopped node overlap





but now group 106 has moved into second column, how can I force all the groups to be in one column so it looks like this

It looks like there are no links going into group 106. There ought to be a link from 55 to 106.

I think I have misunderstood, I thought if I use DeleteEdge & AddEdge methods then there is no visual links deleted or added, because if I add a visual link from group 55 to group 106 then it is not the right representation of my diagram. Here is my MakeNetwork Method

public override LayeredDigraphNetwork MakeNetwork(IEnumerable nodes, IEnumerable links)

{

LayeredDigraphNetwork network = base.MakeNetwork(nodes, links);

foreach (LayeredDigraphEdge e in network.Edges)

{

if (e.Link.FromNode.ContainingSubGraph != null && e.Link.ToNode.ContainingSubGraph != null

&& e.Link.FromNode.ContainingSubGraph != e.Link.ToNode.ContainingSubGraph)

{

network.DeleteEdge(e);

LayeredDigraphEdge newEdge = new LayeredDigraphEdge();

newEdge.FromVertex = network.FindVertex(e.FromVertex.Node.ContainingSubGraph);

newEdge.ToVertex = network.FindVertex(e.ToVertex.Node.ContainingSubGraph);

network.AddEdge(newEdge);



}

}

return network;

}



as you can see from the image in previous post that this code does stop the overlap but does not add any visual link between the 2 groups. All I want to achieve is that links are always drawn vertically and no nodes should overlap each other be it a group or just a normal node

I think you have the right idea. Sorry if I confused you by mentioning “link” when I meant “edge”.

The problem might be that each Group doesn't have its own Layout.

Now I have given a layout for group

go:Group.Layout

<go:LayeredDigraphLayout Direction=“90”/>

</go:Group.Layout>

and the layout is completely out of whack





is there any other workaround\Technique that I can use to achieve my goal i.e. links are always drawn vertically and no nodes should overlap each other

That looks like there’s no edge from the vertex representing 55 to the vertex representing 106.

Also, to be clear, I think you do want nodes to overlap each other: both B's, both C's and the four-dot bar node should be visually inside the 55 node, and similarly for the members of 106 with the 106 node.
Could you check that the Diagram.Layout.LayeredDigraphNetwork contains vertexes for A1, A2, 55, M, and 106 only? That's five vertexes. And that there are only 4 edges, between A1 -- 55, A2 -- 55, 55 -- M, and 55 -- 106?
The Group.Layout's for 55 and the one for 106 don't need any adjustment from the default network, at least for this example.

Sorry for not making myself clear about node overlapping ,I do not want any independent node to be overlapped by a group or other independent node, I did not think of nodes within a group to be overlapping the group they are in, I consider them as contained and hence as just one node i.e. their ContainingSubGraph. So in other words, yes you are right I do want the Bar and B’s & C’s to be overlapping 55 and 106 but don’t want 94, 90, 43, 55, 106 to be overlapping each other



I made a few changes to the Network as suggested



Now Network contains following

Vertexes

--------------------

Node:94

Node:90

Group:55

Node:43

Group:106



Edges

--------------------

Group:55 > Group:106

Group:55 > Node:43

Node:90 > Group:55

Node:94 > Group:55







and diagram looks like this





this is my DataTemplate for the Group



<local:DashedBorder x:Name=“Container” go:Node.LocationElementName=“ContainerGroupPanel”

go:Part.DropOntoBehavior=“AddsToGroup” go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}">





<go:GroupPanel x:Name=“ContainerGroupPanel” Padding=“5 2 5 2” SurroundsMembersAfterDrop=“True”>

</go:GroupPanel>



go:Group.Layout

<go:LayeredDigraphLayout Direction=“90”/>

</go:Group.Layout>

</local:DashedBorder>



DashedBorder is just an extension of ContentControl that Draws a dashed rectangle along the border on Render



I think the problem is that your network-adjusting code isn’t quite right.
Here’s what I tried:

public class GroupingLayout : LayeredDigraphLayout { public override LayeredDigraphNetwork MakeNetwork(IEnumerable<Node> nodes, IEnumerable<Link> links) { var network = base.MakeNetwork(nodes, links); foreach (Link link in links) { if (link.FromNode.ContainingSubGraph != link.ToNode.ContainingSubGraph) { var fromvertex = network.FindVertex(link.FromNode.ContainingSubGraph); if (fromvertex == null) fromvertex = network.FindVertex(link.FromNode); var tovertex = network.FindVertex(link.ToNode.ContainingSubGraph); if (tovertex == null) tovertex = network.FindVertex(link.ToNode); if (fromvertex != null && tovertex != null) { network.DeleteLink(link); network.LinkVertexes(fromvertex, tovertex, null); } } } return network; } }
With this model initialization:

[code] model.NodesSource = new ObservableCollection() {
new SampleNode() { Key=“94” },
new SampleNode() { Key=“90” },
new SampleNode() { Key=“55”, IsSubGraph=true },
new SampleNode() { Key=“43” },
new SampleNode() { Key=“106”, IsSubGraph=true },

    new SampleNode() { Key="55B1", SubGraphKey="55" },
    new SampleNode() { Key="55B2", SubGraphKey="55" },
    new SampleNode() { Key="55bar", SubGraphKey="55" },
    new SampleNode() { Key="55C1", SubGraphKey="55" },
    new SampleNode() { Key="55C2", SubGraphKey="55" },

    new SampleNode() { Key="106B1", SubGraphKey="106" },
    new SampleNode() { Key="106B2", SubGraphKey="106" },
    new SampleNode() { Key="106bar", SubGraphKey="106" },
    new SampleNode() { Key="106C1", SubGraphKey="106" },
    new SampleNode() { Key="106C2", SubGraphKey="106" },
  };
  model.LinksSource = new ObservableCollection<UniversalLinkData>() {
    new UniversalLinkData("94", "55B1"),
    new UniversalLinkData("90", "55B2"),
    new UniversalLinkData("55C1", "43"),
    new UniversalLinkData("55C2", "106B2"),

    new UniversalLinkData("55B1", "55bar"),
    new UniversalLinkData("55B2", "55bar"),
    new UniversalLinkData("55bar", "55C1"),
    new UniversalLinkData("55bar", "55C2"),

    new UniversalLinkData("106B1", "106bar"),
    new UniversalLinkData("106B2", "106bar"),
    new UniversalLinkData("106bar", "106C1"),
    new UniversalLinkData("106bar", "106C2"),
  };[/code]

and with the default NodeTemplate and default LinkTemplate but with this GroupTemplate:

<DataTemplate x:Key="GroupTemplate"> <Border BorderBrush="Black" BorderThickness="1" go:Node.LocationElementName="GroupPanel" go:Part.SelectionAdorned="True"> <StackPanel> <TextBlock Text="{Binding Path=Data}" HorizontalAlignment="Stretch" /> <go:GroupPanel x:Name="GroupPanel" /> </StackPanel> <go:Group.Layout> <go:LayeredDigraphLayout Direction="90" /> </go:Group.Layout> </Border> </DataTemplate>
and this Diagram:

<go:Diagram Grid.Row="1" x:Name="myDiagram" Padding="10" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" GroupTemplate="{StaticResource GroupTemplate}"> <go:Diagram.Layout> <local:GroupingLayout Direction="90" /> </go:Diagram.Layout> </go:Diagram>
I get this result:

Walter thanks for doing the sample. In this sample you have given the diagram a layout whereas in my case diagram does not have a layout, I want to create Nodes and Links and do layout on these nodes and links in DoDrop override of Dragging Tool



I have taken bits from your sample and mixed it with my code and I get following





Here is the XAMl

<UserControl x:Class="CustomSample.CustomSample" <br /> xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" <br /> xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" <br /> xmlns:go="http://schemas.nwoods.com/GoXam" <br /> xmlns:local="clr-namespace:CustomSample"> <br /> <br /> <UserControl.Resources> <br /> <DataTemplate x:Key="GroupTemplate"> <br /> <Border BorderBrush="Black" BorderThickness="1" <br /> go:Node.LocationElementName="GroupPanel" go:Part.SelectionAdorned="True"> <br /> <StackPanel> <br /> <TextBlock Text="{Binding Path=Data}" HorizontalAlignment="Stretch" /> <br /> <go:GroupPanel x:Name="GroupPanel" /> <br /> </StackPanel> <br /> <go:Group.Layout> <br /> <go:LayeredDigraphLayout Direction="90" /> <br /> </go:Group.Layout> <br /> </Border> <br /> </DataTemplate> <br /> </UserControl.Resources> <br /> <br /> <Grid> <br /> <go:Diagram x:Name="myDiagram" <br /> Padding="10" <br /> HorizontalContentAlignment="Stretch" <br /> VerticalContentAlignment="Stretch" <br /> GroupTemplate="{StaticResource GroupTemplate}"> <br /> </go:Diagram> <br /> </Grid> <br /></UserControl>



CodeBehind



using System; <br />using System.Collections.Generic; <br />using System.Collections.ObjectModel; <br />using System.Windows; <br />using System.Windows.Controls; <br />using Northwoods.GoXam; <br />using Northwoods.GoXam.Layout; <br />using Northwoods.GoXam.Model; <br />using Northwoods.GoXam.Tool; <br /> <br />namespace CustomSample <br />{ <br /> public partial class CustomSample : UserControl <br /> { <br /> public CustomSample() <br /> { <br /> InitializeComponent(); <br /> GraphLinksModel<GraphLinksModelNodeData<string>, string, string, UniversalLinkData> model = new GraphLinksModel<GraphLinksModelNodeData<string>, string, string, UniversalLinkData>(); <br /> model.Modifiable = true; <br /> myDiagram.Model = model; <br /> <br /> myDiagram.AllowDrop = true; <br /> myDiagram.DraggingTool = new CustomDraggingTool(); <br /> myDiagram.DraggingTool.DropOntoEnabled = true; <br /> } <br /> } <br /> <br /> public class GroupingLayout : LayeredDigraphLayout <br /> { <br /> public override LayeredDigraphNetwork MakeNetwork(IEnumerable<Node> nodes, IEnumerable<Link> links) <br /> { <br /> var network = base.MakeNetwork(nodes, links); <br /> foreach (Link link in links) <br /> { <br /> if (link.FromNode.ContainingSubGraph != link.ToNode.ContainingSubGraph) <br /> { <br /> var fromvertex = network.FindVertex(link.FromNode.ContainingSubGraph); <br /> if (fromvertex == null) fromvertex = network.FindVertex(link.FromNode); <br /> var tovertex = network.FindVertex(link.ToNode.ContainingSubGraph); <br /> if (tovertex == null) tovertex = network.FindVertex(link.ToNode); <br /> if (fromvertex != null && tovertex != null) <br /> { <br /> network.DeleteLink(link); <br /> network.LinkVertexes(fromvertex, tovertex, null); <br /> } <br /> } <br /> } <br /> return network; <br /> } <br /> } <br /> <br /> public class CustomDraggingTool : DraggingTool <br /> { <br /> public override void DoDrop(DragEventArgs e) <br /> { <br /> base.DoDrop(e); <br /> if (e.Data != null) <br /> { <br /> { <br /> { <br /> Diagram diagram = this.Diagram; <br /> try <br /> { <br /> GraphLinksModel<GraphLinksModelNodeData<string>, string, string, UniversalLinkData> diagramModel = (GraphLinksModel<GraphLinksModelNodeData<string>, string, string, UniversalLinkData>)this.Diagram.Model; <br /> var allNodesAndLinks = diagramModel.CreateDataCollection(); <br /> <br /> // Now create nodes <br /> this.Diagram.StartTransaction("ExternalDrop"); <br /> ObservableCollection<GraphLinksModelNodeData<String>> nodes = new ObservableCollection<GraphLinksModelNodeData<String>>() { <br /> new GraphLinksModelNodeData<String>() { Key="94" }, <br /> new GraphLinksModelNodeData<String>() { Key="90" }, <br /> new GraphLinksModelNodeData<String>() { Key="55", IsSubGraph=true }, <br /> new GraphLinksModelNodeData<String>() { Key="43" }, <br /> new GraphLinksModelNodeData<String>() { Key="106", IsSubGraph=true }, <br /> <br /> new GraphLinksModelNodeData<String>() { Key="55B1", SubGraphKey="55" }, <br /> new GraphLinksModelNodeData<String>() { Key="55B2", SubGraphKey="55" }, <br /> new GraphLinksModelNodeData<String>() { Key="55bar", SubGraphKey="55" }, <br /> new GraphLinksModelNodeData<String>() { Key="55C1", SubGraphKey="55" }, <br /> new GraphLinksModelNodeData<String>() { Key="55C2", SubGraphKey="55" }, <br /> <br /> new GraphLinksModelNodeData<String>() { Key="106B1", SubGraphKey="106" }, <br /> new GraphLinksModelNodeData<String>() { Key="106B2", SubGraphKey="106" }, <br /> new GraphLinksModelNodeData<String>() { Key="106bar", SubGraphKey="106" }, <br /> new GraphLinksModelNodeData<String>() { Key="106C1", SubGraphKey="106" }, <br /> new GraphLinksModelNodeData<String>() { Key="106C2", SubGraphKey="106" }, <br /> }; <br /> ObservableCollection<UniversalLinkData> links = new ObservableCollection<UniversalLinkData>() { <br /> new UniversalLinkData("94", "55B1"), <br /> new UniversalLinkData("90", "55B2"), <br /> new UniversalLinkData("55C1", "43"), <br /> new UniversalLinkData("55C2", "106B2"), <br /> <br /> new UniversalLinkData("55B1", "55bar"), <br /> new UniversalLinkData("55B2", "55bar"), <br /> new UniversalLinkData("55bar", "55C1"), <br /> new UniversalLinkData("55bar", "55C2"), <br /> <br /> new UniversalLinkData("106B1", "106bar"), <br /> new UniversalLinkData("106B2", "106bar"), <br /> new UniversalLinkData("106bar", "106C1"), <br /> new UniversalLinkData("106bar", "106C2"), <br /> }; <br /> <br /> foreach (var item in nodes) <br /> { <br /> diagramModel.AddNode(item); <br /> allNodesAndLinks.AddNode(item); <br /> } <br /> <br /> foreach (var item in links) <br /> { <br /> diagramModel.AddLink(item); <br /> allNodesAndLinks.AddLink(item); <br /> } <br /> <br /> <br /> // perform layout on new nodes <br /> GroupingLayout ldLayout = new GroupingLayout(); <br /> ldLayout.LayerSpacing = 39; <br /> ldLayout.ColumnSpacing = 0; <br /> ldLayout.Direction = 90; <br /> ldLayout.LayeringOption = LayeredDigraphLayering.LongestPathSource; <br /> ldLayout.AggressiveOption = LayeredDigraphAggressive.Less; <br /> ldLayout.InitializeOption = LayeredDigraphInitIndices.Naive; <br /> ldLayout.PackOption = LayeredDigraphPack.Straighten; <br /> ldLayout.ArrangementOrigin = diagram.LastMousePointInModel; <br /> ldLayout.DoLayout(this.Diagram.PartManager.FindNodesForData(allNodesAndLinks), this.Diagram.PartManager.FindLinksForData(allNodesAndLinks)); <br /> <br /> this.Diagram.CommitTransaction("ExternalDrop"); <br /> } <br /> catch (Exception ex) <br /> { <br /> <br /> MessageBox.Show(ex.Message + "\n" + ex.StackTrace); <br /> } <br /> } <br /> } <br /> } <br /> } <br /> <br /> } <br />}



It will work if you just replaced above XAML and codebehind in ClassHierarchy sample and then drag and drop a file from windows explorer onto the diagram at runtime

If you want the whole diagram to be laid out reasonably and don’t want to require users to position all of the nodes manually, it only makes sense if there’s a Diagram.Layout.

In this app there are 2 scenarios

Case 1.

user can drag and drop symbols from pallete and create the connectivity and in this case diagram does not need to perform any layout as it will be the user who will position nodes and connect them appropriately

Case 2.

app has a different view i.e. a Windows Form where they can create new objects and make their connectivity by selecting records from a grid. In this case a user can go to a grid and drop an existing record from a grid onto the diagram and I need to lay it out as best as possible hence I need a layout at that time just for the nodes and links being created with this drag and drop operation so I dont disturb any exiting nodes and links in the diagram aswell not to enforce a layout on any other nodes and links to be created later by the user when he drags and drops from Pallete

Ah, OK, so it is basically all manually laid out, except for newly added nodes. So everything’s fine, then, in the situation you just showed, because the user happened to drop at a point which caused the new nodes to overlap some existing ones.