Given a collection of vertex groups, this class counts the number of edges and the sum of edge weights for the edges between pairs of groups, and for the edges within each group.
Inheritance: GraphMetricCalculatorBase
    //*************************************************************************
    //  Method: getConnectivityBetween()
    //
    /// <summary>
    /// Get whether there is connectivity between two groups in a graph.
    /// </summary>
    /// 
    /// <param name="g1">
    /// Group 1
    /// </param>
    /// 
    /// <param name="g2">
    /// Group 2
    /// </param>
    /// 
    /// <param name="oGraph">
    /// The graph these groups are in
    /// </param>
    /// 
    /// <returns>
    /// True if there is connectivity, false otherwise.
    /// </returns>
    //*************************************************************************

    public bool getConnectivityBetween(GroupInfo g1, GroupInfo g2, IGraph oGraph)
    {

        IntergroupEdgeCalculator m_oIntergroupEdgeCalculator = new IntergroupEdgeCalculator();

        // Currently uses undirected graph
        IList<IntergroupEdgeInfo> oIntergroupEdges =
            m_oIntergroupEdgeCalculator.CalculateGraphMetrics(oGraph,
                new GroupInfo[] { g1, g2 }, false);

        foreach (IntergroupEdgeInfo e in oIntergroupEdges)
        {
            if (e.Group1Index == 0 && e.Group2Index == 1)
            {
                return e.Edges > 0;
        }
        }
        return false;
    }
    CreateGraph
    (
        Boolean bIsDirected
    )
    {
        m_oIntergroupEdgeCalculator = new IntergroupEdgeCalculator();

        m_oGraph = new Graph(bIsDirected ?
            GraphDirectedness.Directed : GraphDirectedness.Undirected);

        m_oVertices = m_oGraph.Vertices;
        m_oEdges = m_oGraph.Edges;
    }
    LayOutGraphUsingForceDirectedGroups
    (
        IGraph oGraph,
        List<GroupInfo> oGroupsToLayout,
        LayoutContext oAdjustedLayoutContext,
        BackgroundWorker oBackgroundWorker
    )
    {
        Debug.Assert(oGraph != null);
        Debug.Assert(oAdjustedLayoutContext != null);

        /////////////////////////////////////////////////////
        // Get the initial position for the group vertices //
        /////////////////////////////////////////////////////

        // Determine how much of the screen to use for groups
        Single areaPerVertex = (Single)GroupRectangleCalculator.GetAreaPerVertex(oAdjustedLayoutContext.GraphRectangle, oGroupsToLayout);
        areaPerVertex /= 2f; // Customise this value if necessary to have more whitespace between groups -- expose as a user control?

        // Create a graph for the groups

        Graph groupInitialGraph = new Graph(GraphDirectedness.Undirected);

        // Convert GroupInfos into vertices that can be laid out
        //IEnumerable<GroupVertex> groupInitialGraphVertices = groupsToLayOut.Select<GroupInfo, GroupVertex>(g => new GroupVertex(g, groupInitialGraph));
        List<GroupVertex> groupInitialGraphVertices = new List<GroupVertex>();

        foreach (GroupInfo groupInfo in oGroupsToLayout)
        {
            GroupVertex groupVertex = new GroupVertex(groupInfo, areaPerVertex);
            groupInitialGraphVertices.Add(groupVertex);
            groupInitialGraph.Vertices.Add(groupVertex);
        }

        Int32 iGroups = oGroupsToLayout.Count;

        // Add edges between groups
        IVertexCollection oVertices = groupInitialGraph.Vertices;
        IEdgeCollection oEdges = groupInitialGraph.Edges;

        IList<IntergroupEdgeInfo> intergroupEdgeInfos = new IntergroupEdgeCalculator().CalculateGraphMetrics(oGraph, oGroupsToLayout, false);
        foreach (IntergroupEdgeInfo interGroupEdgeInfo in intergroupEdgeInfos)
        {
            IVertex groupVertex1 = oVertices.ElementAt<IVertex>(interGroupEdgeInfo.Group1Index);
            IVertex groupVertex2 = oVertices.ElementAt<IVertex>(interGroupEdgeInfo.Group2Index);

            if (groupVertex1 != groupVertex2)
            {
                oEdges.Add(groupVertex1, groupVertex2, false);
            }
        }

        // Use Harel-Koren to compute initial group positions, stored in groupVertex.Location
        ILayout oLayout = new HarelKorenFastMultiscaleLayout();
        oLayout.LayOutGraph(groupInitialGraph, oAdjustedLayoutContext);

        foreach (GroupVertex groupVertex in groupInitialGraph.Vertices)
        {
            // If we have too few groups, automatically push them into the AdjustedLayoutContext
            if (groupInitialGraph.Vertices.Count <= 3)
            {
                groupVertex.updateGroupInfoRectangleFromLocation();
                groupVertex.groupInfo.Rectangle = GraphicsUtil.RectangleFToRectangle(GraphicsUtil.MoveRectangleWithinBounds(groupVertex.groupInfo.Rectangle, oAdjustedLayoutContext.GraphRectangle, false), 1);
                RectangleF r = groupVertex.groupInfo.Rectangle;
                groupVertex.Location = new PointF(r.X + r.Width / 2, r.Y + r.Height / 2);
            }
            // Update the meta-node locations
            groupVertex.updateGroupInfoLocationFromLocation();
        }

        /////////////////////////
        // Remove node overlap //
        /////////////////////////

        //  while ( overlaps exist along proximity graph edges )
        //  {
        //      Create proximity graph (delaunay triangulation)
        //      Minimize proxmity graph overlap stress function
        //  }
        //
        //  while ( any overlaps exist )
        //  {
        //      Create proximity graph (delaunay triangulation)
        //      Find overlaps (scan-line alg)
        //      Augment proximity graph with edges for overlaps
        //      Minimize proxmity graph overlap stress function
        //  }

        Boolean initialOverlapInProximityGraph;
        Int32 iteration = 0;
        Graph lastGraphAfterFirstPhase = groupInitialGraph;
        do
        {
            Graph groupProximityGraph = createGroupVertexProximityGraph(lastGraphAfterFirstPhase);
            lastGraphAfterFirstPhase = groupProximityGraph;
            initialOverlapInProximityGraph = proximityGraphStressMajorization(groupProximityGraph, 1000);
            iteration++;
        } while (initialOverlapInProximityGraph == true);
        

        ///////////////////////////////////////////////////////////////////
        // Transform the layout to get the boxes within the LayoutContext//
        ///////////////////////////////////////////////////////////////////

        Rectangle boundingRectangle = oAdjustedLayoutContext.GraphRectangle;

        Int32 overallLeftOffset = Int32.MinValue;
        Int32 overallTopOffset = Int32.MinValue;
        Int32 overallRightOffset = Int32.MinValue;
        Int32 overallBottomOffset = Int32.MinValue;

        foreach (GroupInfo groupInfo in oGroupsToLayout)
        {
            Rectangle groupRectangle = groupInfo.Rectangle;

            Int32 rectangleLeftOffset = boundingRectangle.Left - groupRectangle.Left;
            if (rectangleLeftOffset > overallLeftOffset)
            {
                overallLeftOffset = rectangleLeftOffset;
            }
            Int32 rectangleTopOffset = boundingRectangle.Top - groupRectangle.Top;
            if (rectangleTopOffset > overallTopOffset)
            {
                overallTopOffset = rectangleTopOffset;
            }

            Int32 rectangleRightOffset = groupRectangle.Right - boundingRectangle.Right;
            if (rectangleRightOffset > overallRightOffset)
            {
                overallRightOffset = rectangleRightOffset;
            }
            Int32 rectangleBottomOffset = groupRectangle.Bottom - boundingRectangle.Bottom;
            if (rectangleBottomOffset > overallBottomOffset)
            {
                overallBottomOffset = rectangleBottomOffset;
            }
        }

        Rectangle newBoundingRectangle = new Rectangle(
            boundingRectangle.X - overallLeftOffset, boundingRectangle.Y - overallTopOffset,
            boundingRectangle.Width + overallLeftOffset + overallRightOffset, boundingRectangle.Height + overallTopOffset + overallBottomOffset);

        // Perform metadata-related tasks.

        GroupMetadataManager.OnLayoutUsingGroupsEnd(lastGraphAfterFirstPhase, oGroupsToLayout,
            this.GroupRectanglePenWidth, this.IntergroupEdgeStyle);

        lastGraphAfterFirstPhase.SetValue(ReservedMetadataKeys.GroupLayoutDrawingInfo,
            new GroupLayoutDrawingInfo(oGroupsToLayout, this.GroupRectanglePenWidth, null));

        //TransformLayout(oGraph, new LayoutContext(newBoundingRectangle), oAdjustedLayoutContext);
        TransformLayout(lastGraphAfterFirstPhase, new LayoutContext(newBoundingRectangle), oAdjustedLayoutContext);

        ///////////////////////////////////
        // Finish with individual groups //
        ///////////////////////////////////

        for (Int32 i = 0; i < iGroups; i++)
        {
            GroupInfo oGroupInfo = oGroupsToLayout[i];

            Rectangle oGroupRectangle = oGroupInfo.Rectangle;
            oGroupRectangle.Inflate(-m_iMargin, -m_iMargin);

            // Do not use Rectangle.IsEmpty() here.  It returns true only if
            // the rectangle is at the origin and has zero width and height.

            if (oGroupRectangle.Width > 0 && oGroupRectangle.Height > 0)
            {
                ICollection<IVertex> oVerticesInGroup = oGroupInfo.Vertices;

                if ( !GetLayoutToUseForGroup(oGraph, oVerticesInGroup).
                    LayOutGraphCore(oGraph, oVerticesInGroup,
                        new LayoutContext(oGroupRectangle), oBackgroundWorker)
                    )
                {
                    // LayOutGraphAsyncCancel() was called.

                    return (false);
                }
            }
            else
            {
                oGroupRectangle = oGroupInfo.Rectangle;
                PointF oVertexLocation;

                if (oGroupRectangle.Width > 0 && oGroupRectangle.Height > 0)
                {
                    // Put the group's vertices at the rectangle's center.

                    oVertexLocation = new PointF(
                        oGroupRectangle.Left + oGroupRectangle.Width / 2,
                        oGroupRectangle.Top + oGroupRectangle.Height / 2
                        );
                }
                else
                {
                    // Put the group's vertices at the rectangle's upper-left
                    // corner.

                    oVertexLocation = new PointF(
                        oGroupRectangle.X, oGroupRectangle.Y
                        );
                }

                foreach (IVertex oVertex in oGroupInfo.Vertices)
                {
                    oVertex.Location = oVertexLocation;
                }
            }
        }

        // Perform metadata-related tasks.

        GroupMetadataManager.OnLayoutUsingGroupsEnd(oGraph, oGroupsToLayout,
            this.GroupRectanglePenWidth, this.IntergroupEdgeStyle);

        return (true);
    }