/// <summary> /// Performs the arrangement of the diagram. /// </summary> public bool Arrange(IGraph graph, LayeredLayoutInfo info, LayoutProgress progress) { _graph = new Graph(graph); _info = info; _progress = progress; _current = 0; _total = 1 /*path finding*/ + 1 /*layers splitting*/ + 1 /*dummify*/ + 6 /*minimize crossings*/ + 4 /*swap pairs*/ + 1 /*layout*/ + 1 /*apply*/ + 1 /*compact*/ + 1 /*dedumify*/; // Update progress if (_progress != null) _progress(_current, _total); // Initialize nodes foreach (GraphNode node in _graph.Nodes) { node.SetData(_Layer, -1); node.SetData(_UBaryCenter, 0.0); node.SetData(_DBaryCenter, 0.0); node.SetData(_ULinkCount, 0); node.SetData(_DLinkCount, 0); node.SetData(_UPriority, 0); node.SetData(_DPriority, 0); node.SetData(_GridPosition, 0); node.SetData(_Dummy, false); } // Initialize links foreach (GraphLink link in _graph.Links) { link.SetData(_DummificationLevel, 0); } // Determine the layer depth Path longestPath = PathFinder.FindLongestPath(graph, info.TimeLimit); if (longestPath == null) return true; // No objects in the graph // Update progress if (_progress != null) _progress(_current++, _total); _layers = new ArrayList(); for (int i = 0; i < longestPath.Nodes.Count; i++) _layers.Add(new ArrayList()); // Distribute nodes to their appropriate layers, // starting with the nodes from the longest path for (int i = 0; i < longestPath.Nodes.Count; i++) { // Find the corresponding node in the internal graph GraphNode node = null; foreach (GraphNode n in _graph.Nodes) { if (n.Node == longestPath.Nodes[i]) { node = n; break; } } node.SetData(_Layer, i); (_layers[i] as ArrayList).Add(node); } foreach (INode node in longestPath.Nodes) { // Find the corresponding node in the internal graph GraphNode gnode = null; foreach (GraphNode n in _graph.Nodes) { if (n.Node == node) { gnode = n; break; } } AddChildren(gnode); } // For all layers whose nodes are > 1 / 20 of the total -> // drop nodes. This will ensure that there are no // too wide layers, thus improving visibility of the graph. int total = 0; bool found; foreach (ArrayList layer in _layers) total += layer.Count; int threshold = total / 10; if (total > 50 && _info.SplitLayers) { found = true; while (found) { found = false; foreach (ArrayList layer in _layers) { if (layer.Count > threshold) { // Find candidates for dropping. // Good candidate is a node which // when moved downwards will be closer // to its children SortedList candidates = new SortedList(); foreach (GraphNode node in layer) { // Calculate total child depth int factor = 0; int nodeLayer = (int)node.GetData(_Layer); foreach (GraphLink link in node.OutLinks) { int childLayer = (int)link.Destination.GetData(_Layer); if (childLayer <= nodeLayer) factor += 1; else factor -= 1; } foreach (GraphLink link in node.InLinks) { int childLayer = (int)link.Origin.GetData(_Layer); if (childLayer <= nodeLayer) factor += 1; else factor -= 1; } if (factor > 0) candidates[factor] = node; } if (candidates.Keys.Count == 0) continue; found = true; ArrayList nextLayer = null; int ilayer = _layers.IndexOf(layer); if (ilayer == _layers.Count - 1) { nextLayer = new ArrayList(); _layers.Add(nextLayer); } else { nextLayer = _layers[ilayer + 1] as ArrayList; } while (layer.Count > threshold) { if (candidates.Keys.Count == 0) break; GraphNode node = candidates.GetByIndex(candidates.Count - 1) as GraphNode; candidates.RemoveAt(candidates.Count - 1); nextLayer.Add(node); layer.Remove(node); node.SetData(_Layer, (int)node.GetData(_Layer) + 1); } } if (found) break; } } } // Check if there are directly related nodes on the same layer found = true; while (found) { found = false; int ilayer = 0; ArrayList nodesToDrop = new ArrayList(); foreach (ArrayList layer in _layers) { nodesToDrop.Clear(); foreach (GraphNode node in layer) { foreach (GraphLink link in node.OutLinks) { if ((int)link.Destination.GetData(_Layer) == ilayer) { // The node's child is on the same layer. // Mark it for dropping if (!nodesToDrop.Contains(link.Destination)) nodesToDrop.Add(link.Destination); found = true; } } } // Drop nodes downwards if (found) { ArrayList curLayer = _layers[ilayer] as ArrayList; ArrayList nextLayer = null; if (ilayer == _layers.Count - 1) { nextLayer = new ArrayList(); _layers.Add(nextLayer); } else { nextLayer = _layers[ilayer + 1] as ArrayList; } foreach (GraphNode node in nodesToDrop) { if (curLayer.Count > 1) { nextLayer.Add(node); curLayer.Remove(node); node.SetData(_Layer, (int)node.GetData(_Layer) + 1); } } break; } ilayer++; } } // Calculate initial grid positions foreach (ArrayList layer in _layers) for (int i = 0; i < layer.Count; i++) (layer[i] as GraphNode).SetData(_GridPosition, (double)i); // Update progress if (_progress != null) _progress(_current++, _total); // Add dummy nodes for all links which cross one or more // layers. Add one dummy node for each crossed layer Dummify(); // Reduce crossings MinimizeCrossings(); // Further reduce crossings through pair swap SwapPairs(); // Arrange nodes Layout(); // Update progress if (_progress != null) _progress(_current++, _total); // Compact levels if (_info.ArrowsCompactFactor != 1) Compact(); // Update progress if (_progress != null) _progress(_current++, _total); Apply(); // Update progress if (_progress != null) _progress(_current++, _total); // Reverse dummification Dedummify(); // Update progress if (_progress != null) _progress(_total, _total); // Update nodes positions foreach (GraphNode node in _graph.Nodes) if (node.Node != null) node.Node.Bounds = node.Bounds; // Update arrow points foreach (GraphLink link in _graph.Links) { if (link.Link != null) { object points = link.GetData(_LinkPoints); if (points != null) link.Link.SetPoints(points as ArrayList); } } return true; }
public bool Arrange(IGraph graph, LayeredLayoutInfo info) { return Arrange(graph, info, null); }