LayOutGraph ( IGraph graph, LayoutContext layoutContext ) { AssertValid(); const String MethodName = "LayOutGraph"; this.ArgumentChecker.CheckArgumentNotNull(MethodName, "graph", graph); this.ArgumentChecker.CheckArgumentNotNull(MethodName, "layoutContext", layoutContext); LayoutContext oAdjustedLayoutContext; if (!GetAdjustedLayoutContext(graph, layoutContext, out oAdjustedLayoutContext)) { return; } // Honor the optional LayOutTheseVerticesOnly key on the graph. ICollection <IVertex> oVerticesToLayOut = GetVerticesToLayOut(graph); if (oVerticesToLayOut.Count > 0) { LayOutGraphCore(graph, oVerticesToLayOut, oAdjustedLayoutContext); LayoutMetadataUtil.MarkGraphAsLaidOut(graph); } }
LayOutComponentInBin ( IGraph oGraph, ICollection <IVertex> oVerticesInComponent, Rectangle oBinRectangle ) { Debug.Assert(oGraph != null); Debug.Assert(oVerticesInComponent != null); AssertValid(); oGraph.SetValue(ReservedMetadataKeys.LayOutTheseVerticesOnly, oVerticesInComponent); // Force the FruchtermanReingoldLayout class to randomize the vertices. LayoutMetadataUtil.MarkGraphAsNotLaidOut(oGraph); ILayout oLayout = new FruchtermanReingoldLayout(); oLayout.Margin = BinMargin; LayoutContext oLayoutContext = new LayoutContext(oBinRectangle); oLayout.LayOutGraph(oGraph, oLayoutContext); }
LayOutGraphCore ( IGraph graph, ICollection <IVertex> verticesToLayOut, LayoutContext layoutContext, BackgroundWorker backgroundWorker ) { Debug.Assert(graph != null); Debug.Assert(verticesToLayOut != null); Debug.Assert(verticesToLayOut.Count > 0); Debug.Assert(layoutContext != null); AssertValid(); Int32 iVertices = verticesToLayOut.Count; ICollection <IEdge> oEdgesToLayOut = GetEdgesToLayOut(graph, verticesToLayOut); // If the graph has already been laid out, use the current vertex // locations as initial values. if (!LayoutMetadataUtil.GraphHasBeenLaidOut(graph)) { // The graph has not been laid out. By default, randomize the // locations of those vertices that are not locked. If the graph // has the FruchtermanReingoldLayoutSelectivelyRandomize key, // however, randomize the locations of those vertices that have // IVertex.Location set to LayoutBase.RandomizeThisLocation and are // not locked. Boolean bSelectivelyRandomize = graph.ContainsKey( ReservedMetadataKeys. FruchtermanReingoldLayoutSelectivelyRandomize); RandomizeVertexLocations(verticesToLayOut, layoutContext, new Random(1), bSelectivelyRandomize); } // Store required metadata on the graph's vertices. InitializeMetadata(verticesToLayOut); Rectangle oRectangle = layoutContext.GraphRectangle; Single fArea = oRectangle.Width * oRectangle.Height; Debug.Assert(iVertices > 0); // The algorithm in Figure 1 of the Fruchterman-Reingold paper doesn't // include the constant C, but it is included in the calculation for k // under the "Modelling the forces" section. Single k = m_fC * (Single)Math.Sqrt(fArea / (Single)iVertices); // The rectangle is guaranteed to have non-zero width and height, so // k should never be zero. Debug.Assert(k != 0); // Use the simple cooling algorithm suggested in the Fruchterman- // Reingold paper. Single fTemperature = oRectangle.Width / 10F; Debug.Assert(m_iIterations != 0); Single fTemperatureDecrement = fTemperature / (Single)m_iIterations; while (fTemperature > 0) { if (backgroundWorker != null && backgroundWorker.CancellationPending) { return(false); } // Calculate the attractive and repulsive forces between the // vertices. The results get written to metadata on the vertices. CalculateRepulsiveForces(verticesToLayOut, k); CalculateAttractiveForces(oEdgesToLayOut, k); Single fNextTemperature = fTemperature - fTemperatureDecrement; // Set the unbounded location of each vertex based on the vertex's // current location and the calculated forces. SetUnboundedLocations(verticesToLayOut, layoutContext, fTemperature, fNextTemperature > 0); // Decrease the temperature. fTemperature = fNextTemperature; // For graphs with many edges, telling NodeXLControl to refresh // its window (which is the end result of calling // FireLayOutGraphIterationCompleted()) can significantly slow down // performance, so don't do it. // // For example, a random graph with 1,000 vertices and 10,000 edges // took 3.6 seconds to lay out when the window was repeatedly // refreshed, but only 2.1 seconds with no refreshes. // // The performance improvement is much smaller for graphs with a // large number of vertices, where the O(V-squared) behavior in // CalculateRepulsiveForces() dominates the layout time. #if false if (backgroundWorker != null) { FireLayOutGraphIterationCompleted(); } #endif } RemoveMetadata(verticesToLayOut); return(true); }
LayOutGraphOnBackgroundWorker ( BackgroundWorker oBackgroundWorker, DoWorkEventArgs oDoWorkEventArgs ) { Debug.Assert(oBackgroundWorker != null); Debug.Assert(oDoWorkEventArgs != null); AssertValid(); Debug.Assert(oDoWorkEventArgs.Argument is LayOutGraphAsyncArguments); LayOutGraphAsyncArguments oLayOutGraphAsyncArguments = (LayOutGraphAsyncArguments)oDoWorkEventArgs.Argument; IGraph oGraph = oLayOutGraphAsyncArguments.Graph; LayoutContext oLayoutContext = oLayOutGraphAsyncArguments.LayoutContext; LayoutContext oAdjustedLayoutContext; if (!GetAdjustedLayoutContext(oGraph, oLayoutContext, out oAdjustedLayoutContext)) { return; } // Honor the optional LayOutTheseVerticesOnly key on the graph. ICollection <IVertex> oVerticesToLayOut = GetVerticesToLayOut(oGraph); Int32 iVerticesToLayOut = oVerticesToLayOut.Count; if (iVerticesToLayOut == 0) { return; } // Binning is supported only if the entire graph is being laid out. if (this.SupportsBinning && m_bUseBinning && iVerticesToLayOut == oGraph.Vertices.Count) { // Lay out the graph's smaller components in bins. GraphBinner oGraphBinner = new GraphBinner(); oGraphBinner.MaximumVerticesPerBin = m_iMaximumVerticesPerBin; oGraphBinner.BinLength = m_iBinLength; ICollection <IVertex> oRemainingVertices; Rectangle oRemainingRectangle; if (oGraphBinner.LayOutSmallerComponentsInBins(oGraph, oVerticesToLayOut, oAdjustedLayoutContext, out oRemainingVertices, out oRemainingRectangle)) { // The remaining vertices need to be laid out in the remaining // rectangle. oVerticesToLayOut = oRemainingVertices; oAdjustedLayoutContext = new LayoutContext(oRemainingRectangle); } else { // There are no remaining vertices, or there is no space // left. oVerticesToLayOut = new IVertex[0]; } } if (oVerticesToLayOut.Count > 0) { // Let the derived class do the work. if (!LayOutGraphCore(oGraph, oVerticesToLayOut, oAdjustedLayoutContext, oBackgroundWorker)) { // LayOutGraphAsyncCancel() was called. oDoWorkEventArgs.Cancel = true; return; } LayoutMetadataUtil.MarkGraphAsLaidOut(oGraph); } }
LayOutSmallerComponentsInBins ( IGraph graph, ICollection <IVertex> verticesToLayOut, LayoutContext layoutContext, out ICollection <IVertex> remainingVertices, out Rectangle remainingRectangle ) { AssertValid(); remainingVertices = null; remainingRectangle = Rectangle.Empty; // This method modifies some of the graph's metadata. Save the // original metadata. Boolean bOriginalGraphHasBeenLaidOut = LayoutMetadataUtil.GraphHasBeenLaidOut(graph); ICollection <IVertex> oOriginalLayOutTheseVerticesOnly = (ICollection <IVertex>)graph.GetValue( ReservedMetadataKeys.LayOutTheseVerticesOnly, typeof(ICollection <IVertex>)); // Split the vertices into strongly connected components, sorted in // increasing order of vertex count. ConnectedComponentCalculator oConnectedComponentCalculator = new ConnectedComponentCalculator(); IList <LinkedList <IVertex> > oComponents = oConnectedComponentCalculator.CalculateStronglyConnectedComponents( verticesToLayOut, graph, true); Int32 iComponents = oComponents.Count; // This object will split the graph rectangle into bin rectangles. RectangleBinner oRectangleBinner = new RectangleBinner( layoutContext.GraphRectangle, m_iBinLength); Int32 iComponent = 0; for (iComponent = 0; iComponent < iComponents; iComponent++) { LinkedList <IVertex> oComponent = oComponents[iComponent]; Int32 iVerticesInComponent = oComponent.Count; if (iVerticesInComponent > m_iMaximumVerticesPerBin) { // The vertices in the remaining components should not be // binned. break; } Rectangle oBinRectangle; if (!oRectangleBinner.TryGetNextBin(out oBinRectangle)) { // There is no room for an additional bin rectangle. break; } // Lay out the component within the bin rectangle. LayOutComponentInBin(graph, oComponent, oBinRectangle); } // Restore the original metadata on the graph. if (bOriginalGraphHasBeenLaidOut) { LayoutMetadataUtil.MarkGraphAsLaidOut(graph); } else { LayoutMetadataUtil.MarkGraphAsNotLaidOut(graph); } if (oOriginalLayOutTheseVerticesOnly != null) { graph.SetValue(ReservedMetadataKeys.LayOutTheseVerticesOnly, oOriginalLayOutTheseVerticesOnly); } else { graph.RemoveKey(ReservedMetadataKeys.LayOutTheseVerticesOnly); } if (oRectangleBinner.TryGetRemainingRectangle( out remainingRectangle)) { remainingVertices = GetRemainingVertices(oComponents, iComponent); return(remainingVertices.Count > 0); } return(false); }