Beispiel #1
0
        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);
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #4
0
        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);
            }
        }
Beispiel #5
0
        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);
        }