private void PositionRank(Graph graph, Rank rank, float xOffset) { var nodes = rank.Column; float totalHeight = nodes.Sum(n => n.ScaledSize); float freespace = 1 * graph.ScaledHeight - totalHeight; float ySpace = freespace / (nodes.Count + 1); // space between each block rank.MinNodeSpace = Math.Min(ySpace, 16); float yOffset = ySpace; foreach (var node in nodes) { node.ScaledLocation.X = xOffset; node.ScaledLocation.Y = 1 * graph.ScaledOffset + yOffset + node.ScaledSize / 2; yOffset += node.ScaledSize + ySpace; } }
internal void Reset(int nodeCount, int edgeCount, int weightMax) { CallGraphOn = false; Nodes.Clear(); Edges.Clear(); NextID = 1; for (int i = 0; i < nodeCount; i++) Nodes.Add(new Node() { ID = NextID++, Weight = RndGen.Next(weightMax), ScaledLocation = new PointF((float)RndGen.NextDouble(), (float)RndGen.NextDouble()) }); for (int i = 0; i < edgeCount; i++) { Edge edge = new Edge(Nodes[RndGen.Next(nodeCount)], Nodes[RndGen.Next(nodeCount)]); if (Edges.Any(e => e.Source == edge.Source && e.Destination == edge.Destination)) continue; Edges.Add(edge); edge.Source.Edges.Add(edge); edge.Destination.Edges.Add(edge); } Graphs.Clear(); Nodes = Nodes.Where(n => !n.Filler).ToList(); Nodes.ForEach(n => n.Rank = null); Edges = Edges.Where(e => !e.Filler).ToList(); Edges.ForEach(e => e.Reset()); // do while still more graph groups to process do { // group nodes into connected graphs Dictionary<int, Node> graph = new Dictionary<int, Node>(); // add first unranked node to a graph Node node = Nodes.First(n => n.Rank == null); node.Layout(graph, 0, new List<Node>(), new List<string>()); // while group contains unranked nodes while (graph.Values.Any(n => n.Rank == null)) { // head node to start traversal node = graph.Values.First(n => n.Rank == null); // only way node could be in group is if child added it, so there is a minrank // min rank is 1 back from the lowest ranked child of the node int? minRank = node.OutboundEdges.Min(e => e.Destination.Rank.HasValue ? e.Destination.Rank : int.MaxValue); node.Layout(graph, minRank.Value - 1, new List<Node>(), new List<string>()); } // normalize ranks so sequential without any missing between int i = -1; int currentRank = int.MinValue; foreach (var n in graph.Values.OrderBy(v => v.Rank)) { if (n.Rank != currentRank) { currentRank = n.Rank.Value; i++; } n.Rank = i; } // put all nodes into a rank based multi-map Dictionary<int, Rank> rankMap = new Dictionary<int, Rank>(); foreach (var n in graph.Values) { int index = n.Rank.Value; if (!rankMap.ContainsKey(index)) rankMap[index] = new Rank() { Order = index }; rankMap[index].Column.Add(n); } // once all nodes ranked, run back through and create any intermediate nodes foreach (var n in graph.Values) { foreach (Edge edge in from e in n.Edges where e.Source == n select e) { Debug.Assert(edge.Source == edge.Destination || edge.Source.Rank < edge.Destination.Rank); // create intermediate nodes if (edge.Destination.Rank.Value > n.Rank.Value + 1) { // change edge destination to temp node, until rank equals true destination Edge tempEdge = edge; int nextRank = n.Rank.Value + 1; Node target = edge.Destination; edge.Destination = new Node(); // used as init for tempNode below do { Node tempNode = tempEdge.Destination; tempNode.ID = NextID++; tempNode.Filler = true; tempNode.Rank = nextRank; tempNode.Weight = 3; tempEdge.Destination = tempNode; tempNode.Edges.Add(tempEdge); rankMap[nextRank].Column.Add(tempNode); graph[tempNode.ID] = tempNode; Nodes.Add(tempNode); tempEdge = new Edge(tempNode, new Node()) // initd above as tempNode { Reversed = edge.Reversed, Filler = true }; Edges.Add(tempEdge); nextRank++; if (nextRank == target.Rank.Value) tempEdge.Destination = target; } while (tempEdge.Destination != target); // TODO do need to add edge to assist in uncross? // dont need to add inbound edge to real node because inbound edges // only traversed in layout which is called before this //target.Edges.Add(tempEdge); } } } Graphs.Add(new Graph() { Ranks = rankMap.Values.OrderBy(r => r.Order).ToList() }); } while (Nodes.Any(n => n.Rank == null)); Graphs = Graphs.OrderByDescending(g => g.Ranks.Sum(r => r.Column.Sum(n => n.Weight))).ToList(); double totalWeight = Nodes.Sum(n => n.Weight); double totalArea = 1; // Width* Height; double weightToPix = totalArea / totalWeight * Reduce; Nodes.ForEach(n => n.ScaledSize = (float)Math.Sqrt(n.Weight * weightToPix)); DoRedraw = true; Invalidate(); }