microsoft/automatic-graph-layout

Setting edge ports pre layout run raising a NullReferenceException

Closed this issue · 3 comments

I am looking through the EdgeRoutingSample project, and notice that after the graph reader has read the file and built the GeometryGraph, ports are then added to the edges should they not exist.

However, in the code below, I recieve a NullReferenceException on the layout.Run(); call:

public void RunSample()
{
	GeometryGraph geometryGraph = new GeometryGraph();
	geometryGraph.Nodes.Add(new Node(CurveFactory.CreateRectangle(100, 100, new Microsoft.Msagl.Core.Geometry.Point(0, 0)), "1"));
	geometryGraph.Nodes.Add(new Node(CurveFactory.CreateRectangle(300, 300, new Microsoft.Msagl.Core.Geometry.Point(0, 0)), "2"));
	geometryGraph.Nodes.Add(new Node(CurveFactory.CreateRectangle(100, 100, new Microsoft.Msagl.Core.Geometry.Point(0, 0)), "3"));

	Edge edge = new Edge(geometryGraph.Nodes[0], geometryGraph.Nodes[1], 0, 0, 1);
	edge.UserData = "1 - 2";
	geometryGraph.Edges.Add(edge);

	edge = new Edge(geometryGraph.Nodes[1], geometryGraph.Nodes[2], 0, 0, 1);
	edge.UserData = "2 - 3";
	geometryGraph.Edges.Add(edge);

	edge = new Edge(geometryGraph.Nodes[0], geometryGraph.Nodes[2], 0, 0, 1);
	edge.UserData = "1 - 3";
	geometryGraph.Edges.Add(edge);

	SugiyamaLayoutSettings layoutSettings = new SugiyamaLayoutSettings();
	layoutSettings.EdgeRoutingSettings.EdgeRoutingMode = Microsoft.Msagl.Core.Routing.EdgeRoutingMode.Spline;
	layoutSettings.NodeSeparation = 125;
	layoutSettings.LayerSeparation = 125;

	foreach (Edge tEdge in geometryGraph.Edges)
	{
		if (tEdge.SourcePort == null)
			tEdge.SourcePort = new FloatingPort(tEdge.Source.BoundaryCurve, tEdge.Source.Center);
		if (tEdge.TargetPort == null)
			tEdge.TargetPort = new FloatingPort(tEdge.Target.BoundaryCurve, tEdge.Target.Center);
	}

	IEnumerable<GeometryGraph> subGraphs = GraphConnectedComponents.CreateComponents(geometryGraph.Nodes, geometryGraph.Edges);
	foreach (GeometryGraph subgraph in subGraphs)
	{
		LayeredLayout layout = new LayeredLayout(subgraph, layoutSettings);
		subgraph.Margins = layoutSettings.NodeSeparation + 600;
		layout.Run();
	}

	Microsoft.Msagl.Layout.MDS.MdsGraphLayout.PackGraphs(subGraphs, layoutSettings);
}

I've placed this method into the EdgeRoutingSample project and this is the stack trace:

AutomaticGraphLayout.dll!Microsoft.Msagl.Routing.SplineRouter.CalculatePortsToShapes() Line 648	C#
AutomaticGraphLayout.dll!Microsoft.Msagl.Routing.SplineRouter.RouteOnRoot() Line 180	C#
AutomaticGraphLayout.dll!Microsoft.Msagl.Routing.SplineRouter.RunInternal() Line 171	C#
AutomaticGraphLayout.dll!Microsoft.Msagl.Core.AlgorithmBase.Run() Line 24	C#
AutomaticGraphLayout.dll!Microsoft.Msagl.Layout.Layered.LayeredLayoutEngine.RunPostLayering() Line 290	C#
AutomaticGraphLayout.dll!Microsoft.Msagl.Layout.Layered.LayeredLayoutEngine.RunInternal() Line 248	C#
AutomaticGraphLayout.dll!Microsoft.Msagl.Core.AlgorithmBase.Run() Line 24	C#
AutomaticGraphLayout.dll!Microsoft.Msagl.Layout.Layered.LayeredLayout.RunInternal() Line 57	C#
AutomaticGraphLayout.dll!Microsoft.Msagl.Core.AlgorithmBase.Run() Line 24	C#
EdgeRoutingSample.exe!EdgeRoutingSample.Program.RunSample() Line 107	C#
EdgeRoutingSample.exe!EdgeRoutingSample.Program.Main() Line 23	C#
void CalculatePortsToShapes() {
	portsToShapes = new Dictionary<Port, Shape>();
	foreach (var shape in root.Descendants)
		foreach (var port in shape.Ports)
			portsToShapes[port] = shape;
	//assign all orphan ports to the root 
	foreach (var port in AllPorts().Where(p => !portsToShapes.ContainsKey(p))) {
		root.Ports.Insert(port);
		portsToShapes[port] = root;
	}
}

The exception is in the line foreach (var port in AllPorts().Where(p => !portsToShapes.ContainsKey(p))) { and is caused by p being null.

I'm thinking this bug is possibly between the keyboard and screen, but you never know... If so, could some pointers be given how to get edges to use ports?

Thanks.

Anybody?

If you comment out the part below then everything works. The code below sets the port at centers of the nodes, but then the layout moves the nodes and the ports are now outside of the nodes. So, the routing fails.
foreach (Edge tEdge in geometryGraph.Edges)
{
if (tEdge.SourcePort == null)
tEdge.SourcePort = new FloatingPort(tEdge.Source.BoundaryCurve, tEdge.Source.Center);
if (tEdge.TargetPort == null)
tEdge.TargetPort = new FloatingPort(tEdge.Target.BoundaryCurve, tEdge.Target.Center);
}

The code that works for me is just
public void RunSample()
{
GeometryGraph geometryGraph = new GeometryGraph();
geometryGraph.Nodes.Add(new Node(CurveFactory.CreateRectangle(100, 100, new Microsoft.Msagl.Core.Geometry.Point(0, 0)), "1"));
geometryGraph.Nodes.Add(new Node(CurveFactory.CreateRectangle(300, 300, new Microsoft.Msagl.Core.Geometry.Point(0, 0)), "2"));
geometryGraph.Nodes.Add(new Node(CurveFactory.CreateRectangle(100, 100, new Microsoft.Msagl.Core.Geometry.Point(0, 0)), "3"));

Edge edge = new Edge(geometryGraph.Nodes[0], geometryGraph.Nodes[1], 0, 0, 1);
edge.UserData = "1 - 2";
geometryGraph.Edges.Add(edge);

edge = new Edge(geometryGraph.Nodes[1], geometryGraph.Nodes[2], 0, 0, 1);
edge.UserData = "2 - 3";
geometryGraph.Edges.Add(edge);

edge = new Edge(geometryGraph.Nodes[0], geometryGraph.Nodes[2], 0, 0, 1);
edge.UserData = "1 - 3";
geometryGraph.Edges.Add(edge);

SugiyamaLayoutSettings layoutSettings = new SugiyamaLayoutSettings();
layoutSettings.EdgeRoutingSettings.EdgeRoutingMode = Microsoft.Msagl.Core.Routing.EdgeRoutingMode.Spline;
layoutSettings.NodeSeparation = 125;
layoutSettings.LayerSeparation = 125;

IEnumerable<GeometryGraph> subGraphs = GraphConnectedComponents.CreateComponents(geometryGraph.Nodes, geometryGraph.Edges);
foreach (GeometryGraph subgraph in subGraphs)
{
	LayeredLayout layout = new LayeredLayout(subgraph, layoutSettings);
	subgraph.Margins = layoutSettings.NodeSeparation + 600;
	layout.Run();
}

Microsoft.Msagl.Layout.MDS.MdsGraphLayout.PackGraphs(subGraphs, layoutSettings);

Hello Lev,

Thanks for the reply.

The code below sets the port at centers of the nodes, but then the layout moves the nodes and the ports are now outside of the nodes. So, the routing fails. I was under the impression that the layout would respect the position of the ports.

Much appreciated. :)


Just in case anybody stumbles upon this question, I have modified the code and appended the following and the routing to ports now works:

Microsoft.Msagl.Layout.MDS.MdsGraphLayout.PackGraphs(subGraphs, layoutSettings);

foreach (GeometryGraph subgraph in subGraphs)
{
	InteractiveEdgeRouter portRouter = new InteractiveEdgeRouter(subgraph.Nodes.Select(n => n.BoundaryCurve), 3, 0.65 * 3, 0);
	portRouter.Run();
	foreach (Edge tEdge in subgraph.Edges)
	{
		FloatingPort port1 = new FloatingPort(tEdge.Source.BoundaryCurve, new Microsoft.Msagl.Core.Geometry.Point(tEdge.Source.BoundingBox.Center.X, tEdge.Source.BoundingBox.Bottom));
		FloatingPort port2 = new FloatingPort(tEdge.Target.BoundaryCurve, new Microsoft.Msagl.Core.Geometry.Point(tEdge.Target.BoundingBox.Center.X, tEdge.Target.BoundingBox.Top));
		ICurve spline = portRouter.RouteSplineFromPortToPortWhenTheWholeGraphIsReady(port1, port2, false, out _);
		DrawSpline(spline);
	}
}